fscanf函数的奇怪行为

4
我正在尝试使用Matlab的fscanf函数读取一个小配置文件中的信息。文件内容如下:
YAcex: 1.000000
YOx: 1.000000
KAce: 1.000000

使用的Matlab代码来解析文件是;
fh = fopen('parameters', 'r');
fscanf(fh, 'YAcex: %f\n')
fscanf(fh, 'YOx: %f\n')
fscanf(fh, 'KAce: %f\n')
fclose(fh);

当调用此脚本时,只有"YAcex"行被正确读取;对于另外两行,fscanf返回[]。如果将YOx和KAce行交换(先写KAce再写YOx),则所有行都可以被fscanf正确读取。

有人能解释一下这种行为吗?

补充信息:输入文件中的换行符是简单的换行符(\n字符,没有\r字符)。

2个回答

8
你的问题在于你每次调用 fscanf 函数时只想读取一个值,但默认情况下它会尝试读取尽可能多的值。请注意以下摘自文档的内容:

fscanf 函数会在整个文件中重新应用格式,并将文件指针定位在文件结束标记处。如果 fscanf 无法将 formatSpec 与数据匹配,则仅读取与其匹配的部分并停止处理。

这意味着第一次调用正确地读取了文件的第一行,但随后它也尝试读取下一行,发现没有与其 格式说明符 完全匹配的内容。它找到了下一行的部分匹配,其中 YOx: 的第一个 Y 与格式说明符中 YAcex: 的开头部分匹配。这个部分匹配将文件指针直接定位到 YOx: 中的 Y 后面,导致下一次调用 fscanf 失败,因为它从 Ox: ... 开始。我们可以用 ftell 来说明这一点:
fh = fopen('parameters', 'r');
fscanf(fh, 'YAcex: %f\n');
ftell(fh)

ans =

    18    % The "O" is the 18th character in the file

当您交换YOx:KAce:行时,下一行的部分匹配不再发生,因此文件指针每次都会停在下一行的开头,并且所有读取操作都将成功。
那么,如何解决这个问题呢?一种选择是始终指定size参数,这样fscanf就不会不必要地重新应用格式说明符。
fh = fopen('parameters', 'r');
fscanf(fh, 'YAcex: %f\n', 1);
fscanf(fh, 'YOx: %f\n', 1);
fscanf(fh, 'KAce: %f\n', 1);
fclose(fh);

另一种选择是在一行中完成所有操作:
fh = fopen('parameters', 'r');
values = fscanf(fh, 'YAcex: %f\n YOx: %f\n KAce: %f\n');
fclose(fh);

values 将是一个3x1的数组,其中包含来自文件的3个值。


根据OP的说法,当YOx和KAce线路交换时,他的代码读取正确。对此有何见解? - Sardar Usama
我似乎无法在2014b上重现这种行为 @Sarder Usama - BillBokeey
尝试在文本文件和MATLAB脚本中交换这些行。https://i.stack.imgur.com/Azdmh.jpg - Sardar Usama
@SardarUsama:搞定了。有点晦涩。我以前好像没遇到过。 - gnovice

1

正如你已经意识到的那样,\r或\r\n可能会导致这种行为。可能的原因类似于此,例如,某些不可见字符(如空格)存在于某个地方。您可以通过将所有内容读取为uint8并查看问题发生的位置来调试此问题:

u8 = fread(fh, inf, '*uint8')';

避免这种问题的一个愚蠢方法是将所有内容作为字符读取,并搜索每个关键字:

fh = fopen('parameters');
ch = fread(fh, inf, '*char')'; % read all as char
fclose(fh);

YAcex = regexp(ch, '(?<=YAcex:\s?)[\d\.]+', 'match', 'once'); % parse YAcex 

你可以相应地解析其他内容。这样做的好处是它对某个位置的空格不太敏感,参数的顺序也不重要。

1
虽然这只是针对小文件的一个简洁解决方案,但是如果你已经知道它们在单独的行上,那么反复使用正则表达式在大文件中查找关键字是一种缓慢的解析方式! - Wolfie
@Wolfie 非常正确。在我看来,另一个答案完美无瑕。如果你认为合适的话,我会删除我的回答。 - Xiangrui Li
由你决定,你的解决方案是有效的,只是可能不是这个问题的最佳解决方案 :) - Wolfie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接