为啥这个 fscanf() 读取垃圾值

Posted

技术标签:

【中文标题】为啥这个 fscanf() 读取垃圾值【英文标题】:Why does this fscanf() read garbage values为什么这个 fscanf() 读取垃圾值 【发布时间】:2022-01-14 13:28:08 【问题描述】:

扫描的文件如下所示:

卡萨布兰卡 1942 D 6.5 4.5 6.0 8.0 7.5 迦百农 2018 年 5.5 4.5 8.0 8.0 6.5 比天堂还陌生 1984 D 6.5 5.5 6.0 8.0 4.5 三种颜色:红色 1994 D 6.5 8.5 6.0 8.0 8.5 再见列宁! 2003 C 7.5 3.5 6.0 8.0 9.5 香水:凶手的故事 2006 年 6.5 5.5 6.0 8.0 5.5 肖申克的救赎 1994 D 7.5 5.5 6.0 8.0 8.5 钢锯岭 2016 年 7.5 7.5 6.0 8.0 7.5 迷失在翻译中 2003 年 6.5 4.5 6.0 8.0 7.5 阿甘正传 1994 D 6.5 9.5 6.0 8.0 6.5 在西雅图不眠不休 1993 R 5.5 4.5 6.0 8.5 7.5 傲慢与偏见 2005 R 7.5 4.5 7.0 8.0 8.5

我试过像这样扫描它,所以基本上我想扫描两行中的所有值并将其绑定到结构中各自的位置:

while(fscanf(f, "%[^\n]s %d %c %f %f %f %f %f", &filmy[licznik].nazwa, &filmy[licznik].rok, &filmy[licznik].rodzaj, &filmy[licznik].oceny[0], &filmy[licznik].oceny[1], &filmy[licznik].oceny[2], &filmy[licznik].oceny[3], &filmy[licznik].oceny[4]) != EOF)

当我稍后使用 printf() 打印它时

int i;for(i = 0; i < N; i++)printf("%s\n%d %c %.1f %.1f %.1f %.1f %.1f\n", filmy[i].nazwa, filmy[i].rok, filmy[i].rodzaj, filmy[i].oceny[0], filmy[i].oceny[1], filmy[i].oceny[2], filmy[i].oceny[3], filmy[i].oceny[4]);

我没有得到与输入文件完全相同的输出,而是得到了这个:

卡萨布兰卡 11801600 0.0 0.0 0.0 0.0 0.0

70 0.0 0.0 0.0 0.0 0.0 F 5 0.0 0.0 0.0 0.0 0.0 P☺┤ 0 0.0 0.0 0.0 0.0 0.0

80 0.0 0.0 0.0 0.0 -0.0 Ü2┤ 6619204 s 0.0 0.0 0.0 0.0 -0.0 k 7274608克 0.0 0.0 0.0 0.0 0.0

5 11791315968.0 0.0 0.0 0.0 0.0

0 0.0 0.0 0.0 0.0 0.0

11801176 1834304256.0 0.0 0.0 0.0 0.0 @§@ 1322953350 0.0 0.0 0.0 0.0 0.0 ► 8 0.0 0.0 0.0 0.0 0.0

很确定 '%[^\n]s' 会导致问题,但我不知道如何在不使用它的情况下扫描包含多个单词的标题。

【问题讨论】:

%[^\n]s 不正确。这试图匹配文字s。你想要%[^\n] fscanf()的参数改成"%[^\n] %d %c %f %f %f %f %f\n",但是读取最后一行还是报错,所以没有得到这个:傲慢与偏见2005 R 7.5 4.5 7.0 8.0 8.5 I'm得到那个: ► 8 0.0 0.0 0.0 0.0 0.0 如果你想解析任何复杂的东西,写一个真正的解析器而不是依赖功能贫乏的scanf。但在这种情况下,使用fgetssscanf 的组合可能没问题。但是您应该始终检查scanf 函数的返回值,以验证它匹配的输入数量。 尝试添加空格并使用" %[^\n]。你可能读得很短。 如果没有那个空格,"%[^\n] %d %c %f %f %f %f %f" 中的%[^\n] 将在 previous 换行处停止。阅读不同格式说明符如何以不同方式处理空白。 comment 中的尾随换行符也是 wrong。 【参考方案1】:

%[^\n]s 不正确。这试图匹配文字 s。你要%[^\n]

此外,您应该检查scanf 写入的转化次数是否与您预期的一样多。例如

while( fscanf(f, " %[^\n] %d %c %f %f %f %f %f", ...) == 8 )

注意第一个转换说明符之前添加的空格。 [ 转换不消耗空格,因此如果输入流中的下一个字符是换行符, fscanf 将立即返回而不读取任何数据。由于您的扫描在上一行的尾随浮点数处停止读取,因此输入流中的下一个字符通常是换行符。

此外,您确实希望通过限制%[^\n] 将读取的数据量来防止缓冲区溢出。您只想读取比正在写入的缓冲区大小少一。所以如果nazwa的大小是512,你应该写:

while( fscanf(f, " %511[^\n] %d %c %f %f %f %f %f", ...) == 8 )

%f%d 也有类似的问题,如果输入包含无法以 int 或 float 表示的值,则行为未定义,但通常不必担心这一点。如果您对此感到担心,则不应使用scanf。不过,您不应该使用scanf,这是不言而喻的。

【讨论】:

以上是关于为啥这个 fscanf() 读取垃圾值的主要内容,如果未能解决你的问题,请参考以下文章

使用 fscanf 时防止缓冲区溢出

C语言函数fscanf()(从流 stream 读取格式化输入)

C语言 fscanf 格式化读取文件中数据

为啥 GCC 生成的代码会从堆栈中读取垃圾?

C语言文件读取fscanf(),该怎么处理

如何使用 fscanf 读取 UTF-8 编码