سؤال

أقوم بتطوير تطبيق يشبه قاعدة البيانات يخزن بنية AA التي تحتوي على:

struct Dictionary
{
    char *key;
    char *value;

    struct Dictionary *next;
};

كما ترون ، أنا أستخدم قائمة مرتبطة لتخزين المعلومات. لكن المشكلة تبدأ عندما يخرج المستخدم من البرنامج. أريد تخزين المعلومات في مكان ما. لذلك كنت أفكر في تخزين القائمة المرتبطة في ملف دائم أو مؤقت باستخدام FOPEN ، ثم عندما يبدأ المستخدم البرنامج ، واسترداد القائمة المرتبطة. فيما يلي الطريقة التي تطبع القائمة المرتبطة بوحدة التحكم:

void PrintList()
{
    int count = 0;
    struct Dictionary *current;

    current = head;

    if (current == NULL)
    {
            printf("\nThe list is empty!");
        return;
    }

    printf("    Key \t  Value\n");
    printf("  ======== \t ========\n");

    while (current != NULL)
    {
        count++;
        printf("%d.  %s \t %s\n", count, current->key, current->value);
        current = current->next;
    }
}

لذلك أفكر في تعديل هذه الطريقة لطباعة المعلومات من خلال FPRINTF بدلاً من printf ، ثم سيحصل البرنامج على المعلومات من الملف. هل يمكن لأحد أن يساعدني في كيفية القراءة والكتابة إلى هذا الملف؟ ما نوع الملف الذي يجب أن يكون مؤقتًا أم عاديًا؟ كيف يمكنني تنسيق الملف (مثلما كنت أفكر في مجرد امتلاك المفتاح أولاً ، ثم القيمة ، ثم شخصية سطر جديد)؟

هل كانت مفيدة؟

المحلول

ربما يجب أن يكون الملف منتظمًا. لا يضمن وجود ملف Temp أن يكون هناك في المرة القادمة التي تبدأ فيها تطبيقك. أيضًا ، يبدو تنسيلك جيدًا للبشر ، وليس جيدًا للآلات. أوصي إما بإنشاء تنسيق الملف الثنائي الخاص بك أو باستخدام XML (أو ربما JSON؟). ربما يمكنك تنسيقه بسهولة مثل

key1\0value1\0key2\0value2\0....

سأكتب مثالًا سريعًا هو رمز psuedoish:

//To write...
Dictionary *this=begin_list;
while(this!=null){
  for(i=0;i<strlen(this->key);i++){
    write_byte(this->key[i]);
  }
  for(i=0;i<strlen(this->value);i++){
    write_byte(this->value[i]);
  }
  this=this->next;
}

//to read...
Dictionary *prev;
Dictionary *this;
char *buffer;
while(!eof){
  buffer=malloc(MAX_STRING_LEN);
  int i=0;
  this=malloc(sizeof(Dictionary)
  while(i<MAX_STRING_LEN){ //note no error checking
    buffer[i]=read_byte();
    if(buffer[i]==0){
      break;
    }
  }
  this->key=buffer;
  buffer=malloc(MAX_STRING_LEN)
  while(i<MAX_STRING_LEN){ //note no error checking
    buffer[i]=read_byte();
    if(buffer[i]==0){
      break; 
    }
  }
  this->value=buffer;
  if(prev!=null){
    prev->next=this;
  }
  this->next=null;
  prev=this;
}

أعلم أنه مثال ضعيف. أعتقد أن ScanF أو ما شابه قد يجعل المهمة أسهل ، لكن مهاراتي C أصبحت صدئة.

نصائح أخرى

القضية الأساسية هي أن المؤشرات لا تترجم إلى التخزين الخارجي. ليس هناك ما يضمن أنه عندما ينفذ البرنامج مرة أخرى ، سيكون له نفس نطاقات الذاكرة (عناوين). بالنظر إلى هذا المبدأ ، هناك طرق بديلة لتخزين بياناتك.

عمليات البيانات المستمرة:
1. استخدم قاعدة بيانات ، صغيرة أو كبيرة.
2. قم بتحويل بياناتك إلى نص ASCII بتنسيق قابل للتكبير.
3. استخدم السجلات الثنائية ذات الطول الثابت
4. استخدم السجلات الثنائية ذات الحجم المتغير
5. قم بتنفيذ بنية بيانات القاموس باستخدام إزاحة الملف بدلاً من المؤشرات.

باستخدام قاعدة بيانات
دع تطبيقًا احترافيًا (تم اختباره وأعماله) يدير بياناتك. يتيح لك هذا التركيز على استخدام البيانات بدلاً من التخزين والتردد.

تحويل إلى تنسيق قابل للسكان
الفكرة هنا هي كتابة البيانات إلى الملف بتنسيق يسهل استرداده وصيانته. ومن الأمثلة على ذلك قيم فاصلة منفصلة (CSV) و XML و INI. يتطلب هذا رمزًا من الجزء الخاص بك لقراءة البيانات وكتابةها. هناك مكتبات للمساعدة.

استخدم السجلات الثنائية ذات الطول الثابت
مع سجلات الطول الثابت ، تتم قراءة البيانات من الملف وإدراجها في القاموس. تكون الملفات الثنائية فعالة للغاية فيما يتعلق بنقل البيانات ، ولكنها ليست محمولة للغاية ، خاصةً عند تغيير إصدارات نظام التشغيل ، أو تغيير نسخ المنصات أو تغيير الإصدارات المترجم. قد يكون هناك مضيعة للمساحة لسجلات النصوص.

استخدم السجلات الثنائية ذات الحجم المتغير
هذه التقنية توفر المساحة ولكنها تزيد من وقت المعالجة. يجب معالجة كل سجل من أجل العثور على موقع السجل التالي. الوصول العشوائي إلى السجلات أمر صعب. خلاف ذلك على غرار السجلات الثنائية ذات الطول الثابت.

تنفيذ بنية بيانات القاموس في الملف
نفس الخوارزمية مثل بنية البيانات القائمة على الذاكرة باستثناء استخدام إزاحة الملفات بدلاً من المؤشرات. يمكن إلحاق سجلات جديدة حتى نهاية الملف. استعادة الإدخالات المحذوفة أمر صعب وسيؤدي إلى تجزئة. يمكن حل التجزئة عن طريق كتابة ملف جديد. إذا كنت تمر بهذا الجهد الكبير ، فيمكنك أيضًا استخدام تطبيق قاعدة بيانات موجود.

إحدى الطرق التي يمكنك من خلالها قراءتها أو الكتابة إلى الملف هي استخدام Freopen مثل هذا: Freopen ("file.out" ، "wt" ، stdout) ، فأنت ستذهب إلى الملف. لن تحتاج إلى تعديل الكود الكثير.

يمكنك تخزين المعلومات في نص عادي ، لكنني أعتقد حقًا أن أفضل طريقة للقيام بذلك هي حفظ المعلومات في ملف ثنائي. يمكنك التحقق من المزيد حول معلومات البحث هذه حول Fread و Fwrite.

إليك طريقة واحدة لحل المشكلة.

قم بإنشاء بنية بيانات لعناصر قائمتك مثل هذا:

struct DictionaryArchive {
    char key[MAX_KEY_LENGTH];
    char value[MAX_VALUE_LENGTH];
    int next;
};

ستحتاج إلى تحديد قيم MAX_KEY_LENGTH و MAX_VALUE_LENGTH وفقًا للبيانات التي ستتوقعها.

الآن ، قم بتحويل قائمتك المرتبطة إلى مجموعة من هذه الهياكل. بدلاً من تخزين مؤشر لتحديد موقع العنصر التالي ، ستقوم بتخزين فهرس الصفيف للعنصر التالي. هذا يحول قائمتك إلى تنسيق حيث يكون كل عنصر حجمًا يمكن التنبؤ به قائمتك بأكملها واحدة متتالية من الذاكرة. الآن انت تستطيع fwrite هذه المجموعة إلى ملف ثنائي لأرشفة ، و fread مرة أخرى لاستعادته.

بديل أكثر كفاءة في الفضاء لاستخدام الحجم الثابت char المصفوفات أعلاه هي تحديد تنسيق ملف مخصص بدلاً من استخدام الهياكل الثابتة. لحالتك ، يمكنك استخدام تنسيق ملف مثل هذا لتخزين بياناتك بطريقة قابلة للاسترداد:

  • تتم كتابة القائمة إلى الملف بالترتيب ، بدءًا من الرأس واتباع next مؤشرات للذيل
  • سيتم تخزين كل عنصر قائمة باستخدام أربعة حقول بيانات بالترتيب التالي:
    1. عدد صحيح 16 بت ، key_length
    2. صفيف شار 8 بت مع key_length عناصر، key_data
    3. عدد صحيح 16 بت ، value_length
    4. صفيف شار 8 بت مع value_length عناصر، value_data

الآن ، يمكنك المشي في القائمة ، وإلقاء بياناتك إلى عقدة الملف بواسطة Node. لإعادة بناء بياناتك ، اقرأ من خلال الملف الثنائي ، وإنشاء جديد struct Dictionary عناصر لكل إدخال ، وربطها معًا بالترتيب الذي تظهر في الملف.

الرمز الخاص بك لكتابة البيانات إلى ملف البيانات سيبدو شيئًا من هذا القبيل (لم يتم اختباره ، لأغراض التوضيح فقط):

FILE* fd;
size_t len;
struct Dictionary* pDict = list_head;
fd = fopen("output_file.dat", "w");

// Walk through the list, storing each node
while (pDict != NULL) {
    // Store key
    len = strlen(pDict->key);
    fwrite(&len, sizeof(len), 1, fd);
    fwrite(pDict->key, len, sizeof(char), fd);

    // Store value
    len = strlen(pDict->value);
    fwrite(&len, sizeof(len), 1, fd);
    fwrite(pDict->value, len, sizeof(char), fd);

    // Move to next list node
    pDict = pDict->next;
};

fclose(fd);

الرمز الخاص بك لقراءة البيانات خارج سيكون متشابهًا جدًا (اقرأ بدلاً من الكتابة ، وإنشاء جديد struct Dictionary كائن لكل تكرار حلقة).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top