Сохраняются изменения в C - PullRequest
1 голос
/ 15 апреля 2010

Я разрабатываю приложение, похожее на базу данных, которое хранит структуру, содержащую:

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, и тогда программа просто получит информацию из файла. Может ли кто-нибудь помочь мне с тем, как я могу читать и писать в этот файл? Что это за файл, временный или обычный? Как мне отформатировать файл (как будто я думал сначала о ключе, затем о значении, затем о символе новой строки)?

Ответы [ 4 ]

2 голосов
/ 15 апреля 2010

Основная проблема заключается в том, что указатели не переводятся во внешнее хранилище.Нет никакой гарантии, что при повторном выполнении вашей программы она будет иметь те же диапазоны памяти (адреса).Учитывая этот принцип, существуют альтернативные методы хранения ваших данных.

Процессы для постоянных данных:
1. Используйте базу данных, маленькую или большую.
2. Преобразуйте вашиданные в текст ASCII в сканируемом формате.
3. Используйте двоичные записи фиксированной длины
4. Используйте двоичные записи переменного размера
5. Реализуйте структуру данных словаря, используя смещения файлов вместо указателей.

Использование базы данных
Позвольте профессиональному приложению (которое было проверено и работает) управлять вашими данными.Это позволяет вам сконцентрироваться на использовании данных, а не на их хранении и извлечении.

Преобразование в формат для сканирования
Идея заключается в том, чтобы записать данные в файл в формателегко найти и поддерживать.Примеры включают значения, разделенные запятыми (CSV), XML и INI.Это требует кода с вашей стороны для чтения и записи данных.Есть библиотеки, чтобы помочь.

Использовать двоичные записи фиксированной длины
При записи фиксированной длины данные считываются из файла и вставляются в ваш словарь.Двоичные файлы очень эффективны для передачи данных, но не очень переносимы, особенно когда меняются версии операционной системы, меняются платформы или версии компилятора.Там может быть пустая трата места для текстовых записей.

Использовать бинарные записи переменного размера
Этот метод экономит место, но увеличивает время обработки.Каждая запись должна быть обработана, чтобы найти местоположение следующей.Произвольный доступ к записям затруднен.В остальном аналогично бинарным записям фиксированной длины.

Реализация словарной структуры данных в файле
Тот же алгоритм, что и в вашей структуре данных на основе памяти, за исключением того, что вместо указателей используются смещения файлов.Новые записи могут быть добавлены в конец файла.Восстановление удаленных записей сложно и приведет к фрагментации.Фрагментация может быть решена путем записи нового файла.Если вы прилагаете столько усилий, вы также можете использовать существующее приложение базы данных.

2 голосов
/ 15 апреля 2010

Файл, вероятно, должен быть обычным. Временный файл не обязательно будет там при следующем запуске приложения. Кроме того, ваш формат выглядит хорошо для людей, не так хорошо для машин. Я бы порекомендовал либо создать свой собственный двоичный формат файла, либо использовать 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 голос
/ 15 апреля 2010

Один из способов чтения или записи в файл - это использование freopen: freopen ("file.out", "wt", stdout), тогда ваши printf перейдут в file.out, и вам не нужно будет много менять код.

Вы можете хранить информацию в виде простого текста, но я действительно считаю, что лучший способ сделать это - сохранить информацию в двоичном файле. Вы можете проверить больше об этой поисковой информации о fread и fwrite.

0 голосов
/ 15 апреля 2010

Вот один из способов решения проблемы.

Создайте структуру данных для элементов списка, например:

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

Теперь вы можете просматривать список, выгружая ваши данные в файл узел за узлом. Чтобы восстановить ваши данные, прочитайте двоичный файл, сгенерируйте новые элементы 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 объект для каждой итерации цикла).

...