【问题标题】:Why does my fscanf keep skipping random numbers?为什么我的 fscanf 总是跳过随机数?
【发布时间】:2014-11-19 04:23:37
【问题描述】:

基本上我需要扫描这个文件中的所有值......它几乎可以这样做,但它似乎跳过了随机值,所以它最终没有正确排列。 (这只是我需要为作业做的一部分,所以你不会“放弃答案”,我仍然想自己解决,只需要一点帮助来解决这个问题,因为我卡住了)

https://www.dropbox.com/sh/wterbuyxm76wwrz/AADEtolX68OFrKELI_lxT8_Ra/assignments%20and%20labs/labs/lab5_inputFile.txt?dl=0

代码如下:

int main()
{
    FILE *fp;
    char x[15];
    float ID [1000];
    int i=0, j=0;
    float homework [1000];
    float lab [1000];
    float midterm [1000];
    float Final [1000];
    int count=0;
    char headers[35];
    char y;

    fp= fopen("lab5_inputFile.txt", "r");

    while (fscanf(fp, "%s", x)!=EOF){
        if (count > 728){
            fscanf(fp, "%f", &ID[i]);
            printf("ID : %.1f\n", ID[i]);
            fscanf(fp, "%f", &homework[i]);
            printf("Homework: %.1f\n", homework[i]);
            fscanf(fp, "%f", &lab[i]);
            printf("lab: %.1f\n", lab[i]);
            fscanf(fp, "%f", &midterm[i]);
            printf("Midterm: %.1f\n", midterm[i]);
            fscanf(fp, "%f", &Final[i]);
            printf("Final: %.1f\n", Final[i]);

            i++;
        }
            count ++;

    }
    printf("count = %d\n", count);
    fclose(fp);

我只扫描了最后几个值以使其更易于阅读/调试,在实际代码中我只会跳过标题,在提交之前我还计划更改许多其他内容,就像我可能会使用指针类型和 malloc 而不是数组和其他一堆东西,但我的主要问题是如何解决我在从文件读取到数组时遇到的问题。

提前致谢!

【问题讨论】:

  • @Mohit Jain “fscanf 返回读取的字符数”不正确。 fscanf() 返回转换的字段数或EOF
  • 养成阅读你正在使用的每个函数的文档的习惯(例如fscanf(3)...)。编译所有警告和调试信息 (gcc -Wall -Wextra -g)。 使用调试器 (gdb)。您无需询问就可以找到您的问题!
  • 为什么代码忽略了前 728 次调用 fscanf() 的结果?
  • 文件的第一行需要忽略,以下任何一行都不建议在顶部附近插入 fgets( ... );越过第一行。然后对 fscanf() 的所有以下调用都需要一个包含前导 ' ' 的格式字符串,因此所有空格都将被消耗/跳过此外,对 fscanf() 的每个调用都需要检查返回值以确保转换成功 I.E. if( 1 != fscanf( ... ) ) { // 处理错误 } 使用 '1' 因为所有对 fscanf() 的调用都只转换一个参数
  • 值得定义一个结构,该结构包含要从文件中的每一行读取的每个项目的字段。然后定义一个包含 1000 个这些结构的数组的实例。这将清理代码并使其更容易跟踪(以及以后维护)代码中发生的事情)

标签: c file-io scanf


【解决方案1】:

您每次都在跳过第一行x。您应该将其重写为:

char x[100];
/* while (fscanf(fp, "%s", x)!=EOF){ */
fgets(x, 100, fp);  /* Skip first line */
for(; /* ever */; ) {
    if(5 == fscanf(fp, "%f%f%f%f%f", &ID[i], &homework[i], &lab[i], &midterm[i], &Final[i])) {
       printf("ID : %.1f\n", ID[i]);
       printf("Homework: %.1f\n", homework[i]);
       printf("lab: %.1f\n", lab[i]);
       printf("Midterm: %.1f\n", midterm[i]);
       printf("Final: %.1f\n", Final[i]);
       i++;
    } else break;
}

Live example here

【讨论】:

  • feof(fp) 方法不对。 OP 的文本文件以'\n' 结尾,您的答案取决于以数字结尾的文件。由于文件以非数字结尾,因此最后一个 fscanf("%f", &Final[i]); 不会设置 EOF,并且会出现额外的 5 个 fscanf()。尝试使用以 '\n' 结尾的文本文件来解决您的问题。而不是if(feof(fp)),测试每个fscanf()的结果,尤其是最后一个。
  • @chux 我尝试读取所有值。如果文件没有读取eof 甚至在读取所有内容后,我增加i 声明读取成功。否则,我至少在fscanf 上宣布不成功。 (虽然 OP 的调试打印会出现不必要的情况)
  • 听起来您没有尝试在建议的输入上运行代码。如果最后一个成功读取的数字后面有空格,feof() 将不为真,然后下一组 5 个fscanf(fp, "%f", ... 将全部失败。由于代码不检查fscanf() 的结果,因此未检测到此失败。在这些失败之后,feof() 将变为 true。需要明确的是,feof() not 是否通知没有更多可用数据,feof() 通知 previous IO 操作检测到没有更多可用数据。
  • "如果文件读完后没有读到eof,我增加i" 一个问题。 1) 代码不知道个人 fscanf() 工作(例如输入非数字) 2) 问题是下一个 fscanf(fp, "%f", &ID[i]); 可能会失败,因为没有更多数字要读取。此代码未检测到该故障,然后可以在通过 if(feof(fp)) break; 中断之前执行 5 次失败 fscanf() 建议查看 stackoverflow.com/questions/5431941/…
  • @chux 感谢分享链接。
【解决方案2】:
#include <stdio.h>
#include <stdlib.h>

struct grade
{
    float ID;
    float homework;
    float lab;
    float midterm;
    float final;
};

#define ARRAYSIZE (1000)

int main()
{
    FILE *fp;
    struct grade studentGrades[ARRAYSIZE] = {{0},{0},{0},{0},{0}};
    int i=0;
    int count=0;


    if( NULL == (fp= fopen("lab5_inputFile.txt", "r") ) )
    {
        perror( "fopen for read of lab5_inputFile.txt" );
        exit(1);
    }

    // implied else fopen successful

    char* dummy = malloc(1000*sizeof(char) );
    if( NULL == dummy )
    {
        perror( "malloc for dummy buffer" );
        exit(2);
    }

    // implied else, malloc successful

    char * firstLine = fgets( dummy, sizeof(dummy), fp ); // step past first line of file
    if( NULL == firstLine )
    {
        perror( "fget of first line of file" );
        exit(3);
    }

    // implied else, fgets successful

    free( dummy );

    int itemCount = 0;
    for ( i = 0; i < ARRAYSIZE; i++ )
    {
        itemCount = fscanf(fp, " %f", &studentGrades[i].ID);
        if( EOF == itemCount ) // Note:making assumption that every line, 
                               // after first, is properly formatted
        {
            break; // exit for loop
        }
        if( 1 != itemCount )
        {
            perror("fscanf");
            exit(4);
        }

        // implied else read of ID successful, not end of file

        printf("ID : %.1f\n", studentGrades[i].ID);

        if( 1 != fscanf(fp, " %f", &studentGrades[i].homework) )
        {
            perror( "fscanf of homework");
            exit(5);
        }

        // implied else, read of homework successful

        printf("Homework: %.1f\n", studentGrades[i].homework);

        if( 1 != fscanf(fp, " %f", &studentGrades[i].lab) )
        {
            perror( "fscanf of lab" );
            exit(6);
        }

        // implied else, read of lab successful

        printf("lab: %.1f\n", studentGrades[i].lab);

        if( 1 != fscanf(fp, " %f", &studentGrades[i].midterm) )
        {
            perror( "fscanf of midterm" );
            exit(7);
        }

        // implied else, read of midterm successful

        printf("Midterm: %.1f\n", studentGrades[i].midterm);

        if( 1 != fscanf(fp, " %f", &studentGrades[i].final) )
        {
            perror( "fscanf of final" );
            exit(8);
        }

        // implied else, read of final successful

        printf("Final: %.1f\n", studentGrades[i].final);

        count ++;

    } // end for

    printf("count = %d\n", count);
    fclose(fp);

    return(0);
}

【讨论】:

  • fgets( dummy, sizeof(dummy), fp ) 将无法正常工作。 sizeof(dummy) 是指针的大小,可能是 4 或 8。建议 #define N 1000 ... fgets( dummy, N, fp )
  • 注意:fscanf(fp, " %f",... 格式的空格不需要。 OTOH这也不是问题。
  • 有趣的是,这段代码为studentGrades 使用了一个本地数组,为dummy 使用了一个分配的数组。很高兴展示各种大型数组定义,否则不清楚为什么会有两种不同的方法。
【解决方案3】:

OP 的方法重复读取“一组非空白文本,然后是 5 个数字”。代码应该读取一次,将其扔掉,然后然后重复读取 5 个数字的组。

读取并折腾第一行,然后使用fgets() 读取直到EOF 的行。使用sscanf()strtod() 扫描缓冲区。

char buf[100];
fgets(buf, sizeof buf, fp);  // Read and toss first line
i = 0;

while (fgets(buf, sizeof buf, fp) != NULL) {  // Read until EOF
  if ((i >= 1000) || (sscanf(buf, "%f%f%f%f%f", &ID[i], &homework[i], &lab[i], 
      &midterm[i], &Final[i]) != 5)) {
    fputs("Quit as too many lines or ill formatted\n", stderr);
    break;
  }
  printf("ID : %.1f\n", ID[i]);
  printf("Homework: %.1f\n", homework[i]);
  printf("lab: %.1f\n", lab[i]);
  printf("Midterm: %.1f\n", midterm[i]);
  printf("Final: %.1f\n", Final[i]);
  i++;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-28
    • 2020-06-16
    • 2013-04-23
    • 2013-06-07
    • 1970-01-01
    • 1970-01-01
    • 2014-05-18
    相关资源
    最近更新 更多