В вашем коде большое количество ошибок.Большинство из них можно преодолеть, просто изменив способ записи и чтения из файла данных.Нет необходимости открывать файл в режиме "w+"
.Вы (1) записываете данные, а затем (2) читаете данные.Так что просто откройте файл первоначально в append mode "a"
(или "ab"
, чтобы явно указать двоичную запись - не требуется, но это делает ваши действия понятными).Затем закройте и снова откройте файл, чтобы прочитать.
Когда вы напишите информацию о своей книге.НЕ пишите "Here are the book details"
в файл (это прервет чтение структуры из файла, если вы не сместите индикатор положения файла за пределы ненужного текста перед началом чтения данных).
Затем закройте файл, вы закончили запись (проверка return из fclose
после записи , чтобы перехватить любую ошибку потока, не сообщенную при проверке каждой записи).Теперь просто снова откройте ваш файл в режиме "r"
(чтение) и зацикливайтесь на каждом чтении структуры и выводите значения на экран.
Вместо того, чтобы просматривать каждую ошибку (включая использование gets()
, которое изв этот день вы будете никогда, никогда никогда делать снова) и fflush (stdin)
, который не поддерживается всеми ОС, за исключением потоков seekable , ниже я включил комментарии, объясняющие, как обрабатыватьпользовательский ввод, запись и последующее чтение.Помимо работы кода, самый важный момент, который я могу сделать, - это проверять каждый ввод и вывод , который делает ваш код.Это сэкономит вам огромное количество времени, когда что-то пойдет не так ...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXT 50 /* if you need a constant #define one (or more) */
#define FNAM "book_list.txt"
#define DETAIL "Here are the book details"
typedef struct book_details {
char book_title[MAXT];
int book_no;
double book_price; /* don't use floating-point variables for currency */
} book_details_t; /* people get upset when you lose $ from rounding. */
int main (int argc, char **argv) { /* use arguments to pass info to main */
char *filename = argc > 1 ? argv[1] : FNAM; /* read from file given as */
book_details_t b = { .book_title = "" }; /* argv[1] or FNAM if none */
size_t len = 0; /* string length to aid with trimming '\n' */
FILE *fp;
fp = fopen (filename, "ab"); /* no need for "w+", "a" (append) will
* let you write new values to file,
* then reopen in "r" to read values
*/
if (fp == NULL) {
perror ("fopen-filename"); /* if the file isn't open, don't just */
return 1; /* output a msg, handle the error. */
}
// fflush(stdin); /* only valid on seekable streams or some OSs */
printf ("Enter Book Title: ");
fgets (b.book_title, MAXT, stdin); /* use fgets - NEVER ever gets */
len = strlen (b.book_title); /* get length of title */
if (len && b.book_title[len-1] == '\n') /* check len & ends with '\n' */
b.book_title[--len] = 0; /* replace '\n' with '\0' (0) */
else if (len == MAXT - 1) { /* otherwise title too long */
fprintf (stderr, "error: title too long.\n"); /* handle error */
return 1;
}
printf ("Enter Book ID Number: ");
if (scanf ("%d", &b.book_no) != 1) { /* must validate scanf return */
fprintf (stderr, "error: invalid book_no.\n"); /* every time! */
return 1;
}
printf("Enter Book Price: "); /* same thing for every input */
if (scanf ("%lf", &b.book_price) != 1) {
fprintf (stderr, "error: invalid book_price.\n");
return 1;
}
printf ("\n%s\n\n", DETAIL); /* don't write to file - will break read */
if (fwrite (&b, sizeof(b), 1, fp) != 1) { /* validate every write */
perror ("fwrite-b");
return 1;
}
if (fclose (fp) == EOF) { /* validate 'close after write' */
perror ("fclose after write");
return 1;
}
if ((fp = fopen (filename, "r")) == NULL) { /* validate open for read */
perror ("open for read");
return 1;
}
while (fread (&b, sizeof(b), 1, fp) > 0)
printf ("%s %d %f\n", b.book_title, b.book_no, b.book_price);
fclose(fp);
return 0;
}
( примечание: , в то время как C99 + автоматически return 0;
в конце main()
, этохорошая идея включить его)
Пример использования / вывода
$ ./bin/struct_read_after_write dat/struct_books.dat
Enter Book Title: Huck Finn
Enter Book ID Number: 10157
Enter Book Price: 19.99
Here are the book details
Huck Finn 10157 19.990000
Добавить следующую книгу:
$ ./bin/struct_read_after_write dat/struct_books.dat
Enter Book Title: Tom Sawyer
Enter Book ID Number: 10156
Enter Book Price: 22.95
Here are the book details
Huck Finn 10157 19.990000
Tom Sawyer 10156 22.950000
Проверить двоичный файлФайл
$ hexdump -Cv dat/struct_books.dat
00000000 48 75 63 6b 20 46 69 6e 6e 00 00 00 00 00 00 00 |Huck Finn.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 ad 27 00 00 3d 0a d7 a3 70 fd 33 40 |.....'..=...p.3@|
00000040 54 6f 6d 20 53 61 77 79 65 72 00 00 00 00 00 00 |Tom Sawyer......|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000070 00 00 00 00 ac 27 00 00 33 33 33 33 33 f3 36 40 |.....'..33333.6@|
00000080
Хорошо, но обратите внимание на потраченное впустую пространство при записи символов MAXT
(50
) для каждого заголовка?(если вы не инициализировали b = { .book_title = "" };
что, по вашему мнению, будет в файле?) Позже вы захотите сериализовать данные и записать в файл только байты, содержащие данные, а перед title
будет стоять числосимволы для чтения - но это на другой день.
Настройка на printf
Форматирование
Вы также можете немного прибавить выходное форматирование, включив ширина поля модификатор и выравнивание по левому краю для заголовка, предоставляя фиксированную ширину для идентификатора и ограничивая точность цены до 2 мест, например,
while (fread (&b, sizeof(b), 1, fp) > 0)
printf ("%-50s %6d $%.2f\n",
b.book_title, b.book_no, b.book_price);
, чтозатем выведите результат tidier:
$ ./bin/struct_read_after_write dat/struct_books.dat
Enter Book Title: Validating All I/O
Enter Book ID Number: 101
Enter Book Price: 99.50
Here are the book details
Huck Finn 10157 $19.99
Tom Sawyer 10156 $22.95
My Dog with Fleas 10150 $9.99
Lucky Cats Have None 10151 $12.49
Fun with C 100 $59.21
Validating All I/O 101 $99.50
Просмотрите все и дайте мне знать, если у вас есть дополнительные вопросы.