Присоединение к файлу в C, чтение в массиве структуры - PullRequest
1 голос
/ 04 декабря 2011

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

#include <stdio.h>
#include <string.h>
#define N 25

int main() {

  struct studrec {
    char name[20], surname[20], sex, date[12];
  } students[N];
  int i, count = 0;
  char another;

  FILE *fileptr;

  for (i = 0; i < 10; i++) {
    puts("Press y to continue without adding new records");
    another = getchar();
    if (another == 'y' || another == 'Y') break;
    while ((another = getchar()) != '\n' && another != EOF);
    puts("Input info");

    puts("Name: ");
    if (fgets(students[i].name, sizeof(students[i].name), stdin) == NULL) return 1;
    students[i].name[strlen(students[i].name)-1] = '\0';

    puts("Surname: ");
    if (fgets(students[i].surname, sizeof(students[i].surname), stdin) == NULL) return 1;
    students[i].surname[strlen(students[i].surname)-1] = '\0';

    puts("Sex (m/f): ");
    students[i].sex = getchar();
    while ((another = getchar()) != '\n' && another != EOF);

    puts("Date (dd.mm.yyyy): ");
    if (fgets(students[i].date, sizeof(students[i].date), stdin) == NULL) return 1;
    students[i].date[strlen(students[i].date)-1] = '\0';
    while ((another = getchar()) != '\n' && another != EOF);
   }
   count = i;

   fileptr = fopen("students.txt", "a+");
   for (i = 0; i < count; i++) fwrite(&students, sizeof(students), 1, fileptr);

   rewind(fileptr);
   for (i = 0; (another = fgetc(fileptr)) != EOF && i < N; i++) {
     fseek(fileptr, -1, SEEK_CUR);
     fread(&students, sizeof(students), 1, fileptr);
   }
   fclose(fileptr);

   count = i;
   for (i = 0; i < count; i++) printf("%20s%20s%4c%15s\n", students[i].name, students[i].surname, students[i].sex, students[i].date);

   return 0;
}

Все работает нормально при записи в новый файл. Выход:

...input procedure...
Press y to continue without adding new records
y
                Liam               James   m     12.03.1987
               Abbey             Trueman   f     23.07.1943
                Hugo               Brown   m     13.05.1947

Но затем, если я запустил его снова и попытался добавить другую запись в существующий файл, программа завершится ошибкой:

...input procedure...
Press y to continue without adding new records
y
               Nadia          Rachmonoff   f     12.07.1934
                                    O|u                  
                  �u                       �              u
                                           �           E�u

Похоже, что новый рекорд установлен для студентов [0], а все остальные элементы удалены. Что я делаю неправильно? Возможно, что-то не так с указателем & студентов. Я попытался с & студентов [я], но он возвратил "ошибка сегментации" после первой итерации. Как я понимаю, адрес & студентов "автоматически" увеличивается до следующего элемента после каждого fread / fwrite. Если это не так, программа не будет работать правильно при первом запуске.

Ответы [ 3 ]

3 голосов
/ 04 декабря 2011

У вас есть несколько проблем с тем, как вы справляетесь с массивами. Прежде всего, эта строка:

for (i = 0; i < count; i++) fwrite(&students, sizeof(students), 1, fileptr);

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

for (i = 0; i < count; i++)
  fwrite(&students[i], sizeof(students[i]), 1, fileptr);

... где размер - это размер одного элемента массива (одной записи), а адрес - это адрес записи, которую вы сейчас пишете, а не адрес целого массив.

У вас есть похожая проблема при чтении данных позже, среди прочего:

rewind(fileptr);
for (i = 0; (another = fgetc(fileptr)) != EOF && i < N; i++) {
  fseek(fileptr, -1, SEEK_CUR);
  fread(&students, sizeof(students), 1, fileptr);
}

Во-первых, fread(&students, sizeof(students), 1, fileptr); следует изменить так же, как fwrite() ранее, на fread(&students[i], sizeof(students[i]), 1, fileptr);. Причины те же: вы читаете по одной записи за раз в цикле, поэтому вам нужно читать этот элемент не только в начало массива каждый раз, но и объем данных, которые вы read должен быть размером этой записи , а не размером всего массива.

Во-вторых, хотя исправление вышеприведенного должно заставить его работать, вы можете легко изменить цикл так, чтобы вам не нужно было читать одиночный char в выражении цикла, чтобы проверить EOF. Вы должны действительно проверить это, проверив возврат из fread() в цикле. Что-то вроде:

rewind(fileptr);
for (i = 0; i < N; i++) {
  if (fread(&students[i], sizeof(students[i]), 1, fileptr) != 1)
    break;
}

Или еще лучше, просто прочитайте N элементов без цикла и используйте возвращаемое значение, чтобы сказать вам, сколько было прочитано:

rewind(fileptr);
i = fread(students, sizeof(students[0]), N, fileptr);
0 голосов
/ 04 декабря 2011

Ваш код ввода неверен, попробуйте

   fwrite(students, sizeof*students, i, fileptr);

   rewind(fileptr);
   for (i = 0; i < N && fread(&students[i], sizeof*students, 1, fileptr); i++);
0 голосов
/ 04 декабря 2011

Далее записывается весь массив из 25 элементов count раз:

for (i = 0; i < count; i++) fwrite(&students, sizeof(students), 1, fileptr);

Есть два способа исправить это:

  1. Вычислите правильный размер, занимаемый элементами count, и запишите их одним вызовом fwrite.
  2. Записывать записи по одному, используя цикл.

То же самое относится и к циклу, который пытается прочитать элементы обратно: каждая итерация читает весь массив из 25 элементов .

...