Программа на C, использующая файл и структуру - PullRequest
0 голосов
/ 01 июня 2018

Я хочу знать, почему код не печатает вывод.В чем разница между двоичным и обычным режимами файлов?

#include<stdio.h>
#include <stdlib.h>
typedef struct book_details
{ 
  char book_title[50];
  int book_no;
  float book_price;
}book_details;

int main()
{
  book_details b;
  FILE *fp;

  fp = fopen("book_list.txt","w+");
  if (fp == NULL)
      printf("File not found");

  fflush(stdin);
  printf("Enter Book Title: \n");
  gets(b.book_title);
  printf("Enter Book ID Number: \n");
  scanf("%d",&b.book_no);
  printf("Enter Book Price: \n");
  scanf("%f",&b.book_price);
  fprintf(fp,"Here are the book details");
  fwrite(&b,sizeof(b),1,fp);
  while (fread(&b,sizeof(b),1,fp) > 0)
      printf("%s %d %f\n",b.book_title,b.book_no,b.book_price);
  fclose(fp);
 }

В чем ошибки?

Ответы [ 2 ]

0 голосов
/ 01 июня 2018

В вашем коде большое количество ошибок.Большинство из них можно преодолеть, просто изменив способ записи и чтения из файла данных.Нет необходимости открывать файл в режиме "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

Просмотрите все и дайте мне знать, если у вас есть дополнительные вопросы.

0 голосов
/ 01 июня 2018

Это происходит потому, что здесь же File Pointer fp используется для чтения и записи. Ваш выходной файл является двоичным файлом, поэтому здесь можно использовать только fread() и fwrite().Вы не можете использовать fprintf(fp,"Here are the book details"); в этом случае. Это также вызывает ошибку при чтении . В таких случаях есть ДВА решения.

  1. Использованиеrewind ().

Используя функцию rewind(), мы можем перемотать File Pointer fp обратно в исходное состояние, чтобы прочитать файл.

Попробуйте этот код: -

#include<stdio.h>
#include <stdlib.h>
typedef struct book_details
{ 
  char book_title[50];
  int book_no;
  float book_price;

}book_details;

int main()
{
  book_details b;
  FILE *fp;

  fp = fopen("book_list.txt","r+");
  if (fp == NULL)
     printf("File not found");

  printf("Enter Book Title: \n");
  gets(b.book_title);
  printf("Enter Book ID Number: \n");
  scanf("%d",&b.book_no);
  printf("Enter Book Price: \n");
  scanf("%f",&b.book_price);            // removed fprintf();
  fwrite(&b,sizeof(b),1,fp);
  fflush(stdin);

  rewind(fp);                        // Using rewind();

  while (fread(&b,sizeof(b),1,fp) > 0)
     printf("%s %d %f\n",b.book_title,b.book_no,b.book_price);
  fclose(fp);
 }
Использование отдельных указателей чтения и записи FILE.

Попробуйте этот код: -

#include<stdio.h>
#include <stdlib.h>
typedef struct book_details
{ 
  char book_title[50];
  int book_no;
  float book_price;

}book_details;

int main()
{
  book_details b;
  FILE *fpwrite,* fpread;                 // separate File pointers. 

  fpwrite = fopen("book_list.txt","w");
  if (fpwrite == NULL)
     printf("File not found");

  printf("Enter Book Title: \n");
  gets(b.book_title);
  printf("Enter Book ID Number: \n");
  scanf("%d",&b.book_no);
  printf("Enter Book Price: \n");
  scanf("%f",&b.book_price);                 // removed fprintf();
  fflush(stdin);
  fwrite(&b,sizeof(b),1,fpwrite);
  fclose(fpwrite);

  fpread = fopen("book_list.txt","r");
  while (fread(&b,sizeof(b),1,fpread) > 0)
     printf("%s %d %f\n",b.book_title,b.book_no,b.book_price);
  fclose(fpread);
 }

Отдельные указатели файлов считаются лучше, поскольку они улучшают Readability изисходный код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...