【问题标题】:strange behavior of the fscanf functionfscanf 函数的奇怪行为
【发布时间】:2017-08-21 12:33:20
【问题描述】:

我正在尝试使用 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字符)。

【问题讨论】:

    标签: matlab scanf


    【解决方案1】:

    您的问题是每次调用fscanf 时您只想读取一个值,但默认情况下它会尝试读取尽可能多的值。请注意以下文档摘录:

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

    这意味着第一次调用正确地读取了文件的第一行,但随后也尝试读取下一行,没有找到与其format specifier 匹配的精确。它找到下一行的部分匹配,其中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 argument,这样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 将是一个 3×1 数组,其中包含文件中的 3 个值。

    【讨论】:

    • 根据 OP,当 YOx 和 KAce 线路切换时,他的代码可以正确读取。对此有何见解?
    • 我似乎无法在 2014b @Sarder Usama 上重现这种行为
    • 尝试在文本文件和 MATLAB 脚本中切换这些行。 i.stack.imgur.com/Azdmh.jpg
    • @SardarUsama:想通了。这有点晦涩难懂。我想我以前没有遇到过。
    【解决方案2】:

    正如您已经意识到的那样,\r 或 \r\n 可能会导致这种行为。可能的原因与此类似,例如,某处有一些不可见的字符,如空格。您可以通过将所有内容读取为 uint8 来进行调试,并查看出现问题的位置:

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

    避免此类问题的一种愚蠢方法是将所有内容读取为 char,然后搜索每个关键字:

    fh = fopen('parameters');
    ch = fread(fh, inf, '*char')'; % read all as char
    fclose(fh);
    
    YAcex = regexp(ch, '(?<=YAcex:\s?)[\d\.]+', 'match', 'once'); % parse YAcex 
    

    您可以相应地解析其他人。这样做的好处是它对某处的空间不太敏感,而且参数的顺序无关紧要。

    【讨论】:

    • 虽然这只是小文件的巧妙解决方案,但是当您知道它们在单独的行上时,反复正则表达式大文件的关键字是一种缓慢的解析方式!
    • @Wolfie 非常真实。另一个答案是完美的 IMO。如果您觉得合适,我将删除我的答案。
    • 由你决定,你的解决方案是有效的,它可能不是这个问题的最佳解决方案:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-03
    • 2011-06-14
    • 2020-01-31
    • 2014-05-24
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    相关资源
    最近更新 更多