C程序使用文件和结构

时间:2018-06-01 05:21:24

标签: c file struct

我想知道代码为什么不打印输出。 二进制文件模式和普通文件模式有什么区别?

#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 :(得分:0)

发生这种情况是因为同一File Pointer fp用于读写。您的输出文件是二进制文件,因此此处只能使用fread()fwrite()。在这种情况下,您无法使用fprintf(fp,"Here are the book details");。这也会导致错误读取。在这种情况下,有 TWO 解决方案。

  
      
  1. 使用倒带()。
  2.   

使用函数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);
 }
  
      
  1. 使用单独的读写FILE指针。
  2.   

试试这段代码: -

#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

答案 1 :(得分:0)

您的代码中存在大量错误。只需更改写入方式然后从数据文件中读取即可克服大多数问题。无需在"w+"模式下打开文件。您(1)写入数据,然后(2)读取数据。因此,只需在追加模式"a"(或"ab"中打开文件,以明确指定二进制写入 - 不是必需的,但它确实可以使您的操作变得清晰)。然后关闭并再次打开文件进行阅读。

当您写出书籍信息时。不要将"Here are the book details"写入文件(除非在开始读取数据之前将文件位置指示符偏移到不需要的文本之外,否则它将从文件中删除对struct的读取)。

然后关闭您的文件,您已完成编写(在写入后验证fclose返回以捕获在您验证每个时未报告的任何流错误写)。现在只需在"r"(读取)模式下再次打开文件,然后遍历每个结构读取并将值输出到屏幕。

而不是覆盖每一个错误(包括使用gets()从今天开始,永远不会再再次执行)和fflush (stdin)不支持除了可搜索流之外,所有操作系统都有,我在下面列出了注释,解释了如何处理用户输入,写入和后续读取。除了代码的工作原理之外,我能做的最重要的一点是验证代码所做的每一个输入和输出。当出现问题时,这将为您节省大量时间......

#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

一切都很好,但请注意为每个标题写MAXT50)个字符会浪费空间吗? (如果你没有初始化b = { .book_title = "" };你认为文件中会有什么?)稍后你会想要序列化数据并只将包含数据的字节写入文件,并在title之前按要读的字符数 - 但这是另一天。

调整为printf格式

您可能还希望通过包含字段宽度修饰符和标题的左对齐来整理输出格式,为ID提供固定宽度,并限制精度。价格为2位,例如

    while (fread (&b, sizeof(b), 1, fp) > 0)
        printf ("%-50s %6d    $%.2f\n", 
                b.book_title, b.book_no, b.book_price);

然后会产生更整洁的输出:

$ ./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

仔细看看,如果您有其他问题,请告诉我。