简体   繁体   English

fscanf:如果没有返回EOF,可能是错误还是EOF?

[英]fscanf: is error or EOF possible without EOF returned?

I have a C++ program that reads from a file that I expect to have a bunch of identically formatted records. 我有一个C ++程序,它从一个文件中读取,我希望它有一堆格式相同的记录。 I want to stop reading if I encounter something unexpected whether that means an ill-formatted record or some input failure and I want to differentiate between these different conditions. 如果我遇到意外的事情,我想停止阅读是否意味着格式错误的记录或输入失败,我想区分这些不同的条件。

I've seen this answer and looked at the fscanf() documentation and I'm not sure if fscanf() can indicate an error or EOF without returning EOF. 我已经看到了这个答案并查看了fscanf() 文档 ,我不确定fscanf()可以指示错误或EOF而不返回EOF。 From my understanding of both those links it's possible that an error occurs or EOF occurs even if fscanf() returns a value of 0 or greater so I would have to call ferror() and feof() regardless of what value fscanf() returns. 从我这两个环节的理解有可能发生错误或EOF即使发生fscanf()返回值为0或更大的值,所以我得打个电话ferror()feof()无论什么价值的fscanf()返回。 I can't seem to find how a return value of EOF is of any use to the caller. 我似乎无法找到EOF的返回值如何对调用者有任何用处。

Say I expect my file to have a bunch of records with 4 values. 假设我希望我的文件有一堆包含4个值的记录。 Would the code below properly handle any end of file and input error conditions? 下面的代码会正确处理文件的任何结尾和输入错误条件吗?

  int ret;
  int field1;
  int field2;
  int field3;
  int field4;
  while ((ret = fscanf(pFile, "%d %d %d %d", &field1, &field2, &field3,
                       &field4)) == 4) {
    // do whatever with fields
  }
  if (ferror(fp)) {
    // some input error occurred
  } else if (feof(fp)) {
    // end of file occurred
  } else {
    assert(ret != EOF);
    // encountered record that didn't match expected format
  }

Update: So I'm going to add the documentation from cppreference because it seems to differ slightly in describing what condition no longer causes EOF to be returned. 更新:所以我将添加来自cppreference的文档,因为在描述哪些条件不再导致返回EOF时似乎略有不同。

Is error or EOF possible without EOF returned? 没有退回EOF会出现错误或EOF吗?

Yes it is. 是的。 You could also get a return value between 0 and 3. cplusplus.com is somewhat notoriously sloppy. 您还可以获得0到3之间的返回值.cplusplus.com有点臭名昭着。 Let's take a look at cppreference.com's page instead. 我们来看看cppreference.com的页面

Return value: Number of receiving arguments successfully assigned (which may be zero in case a matching failure occurred before the first receiving argument was assigned), or EOF if input failure occurs before the first receiving argument was assigned. 返回值:成功分配的接收参数数(如果在分配第一个接收参数之前发生匹配失败,则可能为零),如果在分配第一个接收参数之前发生输入失败,则为EOF。

There are a couple different scenarios. 有几种不同的场景。 Let's break it down into cases: 让我们把它分解成案例:

  1. If it has successfully assigned to the first receiving argument then you'll get a positive value, guaranteed. 如果它已成功分配给第一个接收参数,那么您将获得正值,保证。 Let's say it's assigned to two variables and then hits EOF. 假设它被分配给两个变量,然后命中EOF。 It'll return 2 and feof() will return true. 它将返回2并且feof()将返回true。

  2. Otherwise, if it hasn't assigned to the first receiving argument and gets a matching failure it'll return 0 . 否则,如果它尚未分配给第一个接收参数并获得匹配失败,则它将返回0 What's a matching failure? 什么是匹配失败? That's when it's matching a specifier like %d and doesn't get a valid integer. 那时它匹配像%d这样的说明符并且没有得到有效的整数。 If the input is foobar then %d will fail to match. 如果输入是foobar%d将无法匹配。

    Or, less commonly, it's when it's looking for a literal character and doesn't see it. 或者,不太常见的是,当它正在寻找文字字符并且没​​有看到它时。 For instance if your format string expected each number to be enclosed in brackets ( "[%d] [%d] [%d] [%d]" ) then it would return 0 if the input didn't start with [ . 例如,如果您的格式字符串要求将每个数字括在括号中( "[%d] [%d] [%d] [%d]" ),那么如果输入没有以[开头"[%d] [%d] [%d] [%d]" ,它将返回0

  3. Otherwise, if it hasn't assigned to the first receiving argument and gets a EOF or a read error it'll return EOF . 否则,如果它尚未分配给第一个接收参数并获得EOF或读取错误,则它将返回EOF Note that a read error is different from a matching error. 请注意, 读取错误与匹配错误不同。 A read error is when the OS returns an I/O error trying to read from disk. 读取错误是指操作系统返回尝试从磁盘读取的I / O错误。

    A return value of EOF signals either end-of-file or I/O error. EOF的返回值表示文件结束或I / O错误。 If you don't care about the distinction you could just abandon your loop and move on with the program. 如果您不关心这种区别,您可以放弃循环并继续使用该程序。 If, however, you want to print an error message on ferror() and treat feof() as success then checking ret isn't sufficient; 但是,如果要在ferror()上打印错误消息并将feof()视为成功,则检查ret是不够的; you have to call one or both of those functions. 你必须调用其中一个或两个功能。 It's up to you if you want to do that or not. 如果你想这样做,这取决于你。

Say I expect my file to have a bunch of records with 4 values. 假设我希望我的文件有一堆包含4个值的记录。 Would the code below properly handle any end of file and input error conditions? 下面的代码会正确处理文件的任何结尾和输入错误条件吗?

Yep. 是的。 Looks good to me. 对我来说看上去很好。


For what it's worth, I recommend against using scanf() and fscanf() . 对于它的价值,我建议不要使用scanf()fscanf() They're complicated and make handling input errors robustly more difficult than necessary. 它们很复杂,并且使处理输入错误比必要时更加困难。 This question is a great demonstration. 这个问题是一个很好的证明。 It's better to use fgets() to read an entire line and sscanf() to parse it. 最好使用fgets()读取整行,使用sscanf()来解析它。 That way if there's bad input you don't have a partial line sitting around jamming up future reads. 这样一来,如果输入结果不好,你就不会有局部线条干扰未来的读取。

I find it easier to explain the return value of the fscanf family of functions with examples. 我发现通过示例更容易解释fscanf函数族的返回值。

// The string contains valid data given the format specifier.
// Expect ret to be 1.
int ret = sscanf("10", "%d", &n);

// The string contains data but not what the user expects to see.
// Expect ret to be 0.
int ret = sscanf("abcd", "%d", &n);

// The string contains no data .
// Expect ret to be EOF.
int ret = sscanf("", "%d", &n);

In all these cases, you can expect the same behavior if you have leading spaces. 在所有这些情况下,如果您有前导空格,则可以预期相同的行为。

// Expect ret to be 1.
int ret = sscanf("  10", "%d", &n);

// Expect ret to be 0.
int ret = sscanf("  abcd", "%d", &n);

// Expect ret to be EOF.
int ret = sscanf("  ", "%d", &n);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM