وظيفة C واحدة لكتابة البيانات كنص أو ثنائي
سؤال
لدي وظيفة C التي تكتب بعض البيانات إلى ملف نصي. تتكون البيانات من العوامات ، ints والسلاسل. يبدو شيئًا كهذا:
writeAsTextFile( mystruct_t* myStructWithIntsFloatsAndStrings , const char* fileName);
للقيام بذلك ، أستخدم المكالمات إلى FPRINTF ؛
الآن أود أن أكتب نفس البيانات ولكن ثنائي. يمكنني كتابة وظيفة ثانية لكتابة واستخدام المكالمات للكتاب بدلاً من ذلك. ولكن في كل مرة سأقوم بتغيير تصميم MyStruct_T ، سأضطر إلى تعديل كل من WriteAstextFile و Writeassbinaryfile. وبالطبع فإن ReadastExtFile المقابلة و ReadAsbinaryFile. علاوة على هذا سيزيد هذا الرموز. لذلك أود الحصول على وظيفة عامة واحدة مع وسيطة واحدة أو نص واحد والتي ستبدو هكذا:
writeToFile( mystruct_t* myStructWithIntsFloatsAndStrings , const char* fileName, myEnumType_t eOption)
حيث سيكون الخيار عبارة عن eBin = 0 و etxt = 1 على سبيل المثال. اعتمادًا على eoption ، ستقوم الوظيفة بكتابة بيانات ثنائية أو نصية.
لست متأكدًا مما قد يكون أفضل طريقة لتحقيق ذلك. هل يجب أن أستخدم Fwrite أيضًا للكتابة كنص ، هل يجب أن أحاول استخدام وحدات الماكرو؟ (لقد رأيت استخدام التوجيه ## في مكان ما ولكن لم أستخدمه أبدًا) ، أو عبارات التبديل/ifs في كل مكان أحتاج إلى الكتابة إلى ملف؟ أو يجب أن أكتب وظيفة عامة مثلmyWriteFunction( void *data, char const type, myEnumType_t eOption)
التي سيتم استدعاؤها بواسطة WriteTofile؟
أنا لست على دراية باستخدام Fread/Fwrite و Macros ، لذا فإن أي تعليقات أفضل الممارسات والأفكار وما إلى ذلك هي موضع ترحيب ،
شكرًا
بابا
المحلول
يمكنك إنشاء بضع وظائف لكتابة أنواع مختلفة من البيانات في struct
:
writeInt(File *f, myEnumType_t eOption, int data);
writeFloat(File *f, myEnumType_t eOption, float data);
writeFloatArray(File *f, myEnumType_t eOption, float *data, size_t n_data);
.. ثم يتم إخفاء اختبار الثنائي أو النص في كل من هؤلاء. ستبدو وظيفة كتابة الهيكل الرئيسية الخاصة بك (مع حذف الخطأ):
writeToFile(mystruct_t *myStruct, const char *fileName, myEnumType_t eOption)
{
const char *fmode = eOption == EOPT_BIN ? "wb" : "w";
FILE *f = fopen(filename, fmode);
writeInt(f, eOption, myStruct->a);
writeInt(f, eOption, myStruct->b);
writeFloatArray(f, eOption, myStruct->values, myStruct->n_values);
/* ... */
}
لذا فإن التغيير في تخطيط الهيكل يجب أن يغير مكانًا واحدًا فقط.
يمكنك أيضًا تنفيذ وظائف الكتابة المختلفة لمختلف "أنواع" مستوى التطبيق - على سبيل المثال writeTemperature()
قد تكون متميزة عن عام writeFloat()
.
نصائح أخرى
لموقفك ، ببساطة جعل وظيفة التفاف:
writeToFile(...,bool isBinary) {
if (isBinary) {
// write as binary file
} else {
// write as text file
}
}
بقدر ما تذهب وحدات الماكرو ، فهي مفيدة فقط إذا كنت تريد أن تكون جميع العمليات إما ثنائية أو نص:
#ifdef __BINARY
#define WriteToFile(a,b,c,d,e) WriteToBinary(a,b,c,d,e)
#else
#define WriteToFile(a,b,c,d,e) WriteToText(a,b,c,d,e)
#endif
يستخدم هذا في WinAPI للتبديل بين وظائف ASCII ووظائف الأحرف الواسعة.
راجع للشغل: إذا كان بنيتك يحتوي على char* أو std :: string ، فلن يتم نسخ محتويات السلسلة ، فقط عنوانه. ينطبق هذا على أي مؤشرات أخرى أيضًا ، مثل int* أو std :: vector.
أولاً ، لا تجمع بين الوظائف ، لا يوجد مدخرات عملية.
ثانياً ، سيتعين عليك كتابة كتابة جديدة كوظيفة نصية في كل مرة تستخدم فيها الهيكل ، لا توجد طريقة للتغلب عليها (باستثناء بعض مكتبة التسلسل غير القياسية).
ثالثًا ، طالما لم تقم أبدًا بتغيير ترتيب أعضاء الهيكل ، وإضافة أعضاء جدد فقط إلى نهاية الهيكل ، فلن تحتاج أبدًا إلى كتابة كاتب أو قارئ ثنائي جديد. من أجل تحقيق ذلك ، يجب عليك كتابة حجم الهيكل إلى الملف ثم كتابة الهيكل إلى الملف.
وبهذه الطريقة ، ستقرأ وظيفة القراءة حجم الهيكل (عند كتابتها) وستعرف عدد البايتات التي يجب قراءتها. إذا تغير هيكلك ، فسيتمكن البرنامج الخاص بك من قراءة أجزاء من الإصدار من الهيكل القديم ، وسيكون الأعضاء الجدد في نهاية الهيكل غير ضروري.
تعديل
كتابة بنية مع مؤشرات سوف تكتب قيمة المؤشر. يجب أن تكون حذراً للغاية عند قراءة الهيكل ، لأن المؤشر سيشير إلى سلام عشوائي في الذاكرة بشكل أساسي
إذا كنت ترغب في الحفاظ على العلاقات بين الهياكل ، فسيتعين عليك الخروج بآليتك الخاصة. هذا يختلف في الصعوبة ، وسيتعين عليك التوصل إلى ترتيب محدد مسبقًا لكتابة الهياكل في ، وإعادة بناء جميع المؤشرات عند قراءة البنية.