fscanf:如果没有返回EOF,是否可能发生错误或EOF?

时间:2019-06-15 20:30:30

标签: c++ scanf stdio

我有一个C ++程序,该程序从一个文件中读取,我希望它有一堆格式相同的记录。如果遇到意外情况(表示记录格式错误或某些输入失败),我想停止阅读,我想区分这些不同的情况。

我已经看过this answer并看了fscanf() documentation,但不确定fscanf()是否可以指示错误或EOF而不会返回EOF。根据我对这两个链接的理解,即使fscanf()返回0或更大的值,也可能会发生错误或发生EOF,因此无论如何,我都必须调用ferror()feof()fscanf()返回。我似乎找不到EOF的返回值对调用者有什么用。

说我希望我的文件中有一堆带有4个值的记录。下面的代码能正确处理文件结尾和输入错误的情况吗?

  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
  }

更新:因此,我将添加cppreference中的文档,因为它在描述什么条件不再导致EOF返回方面似乎略有不同。

2 个答案:

答案 0 :(得分:3)

  

如果没有返回EOF,是否可能发生错误或EOF?

是的。您也可能会得到0到3之间的返回值。cplusplus.com有点草率。让我们来看一下cppreference.com's page

  

返回值:成功分配的接收参数的数量(如果在分配第一个接收参数之前发生匹配失败,则为零);如果在分配第一个接收参数之前发生输入失败,则为EOF。 / em>

有两种不同的情况。让我们将其分解为几种情况:

  1. 如果已成功将其分配给第一个接收参数,那么您将获得肯定的值。假设它已分配给两个变量,然后达到EOF。它将返回2,而feof()将返回true。

  2. 否则,如果未将其分配给第一个接收参数并获得匹配失败,则它将返回0。什么是匹配失败?那是当它与%d之类的说明符匹配并且没有得到有效的整数时。如果输入为foobar,则%d将不匹配。

    或更不常见的是,它正在寻找文字字符而看不到它。例如,如果您的格式字符串希望每个数字都用括号("[%d] [%d] [%d] [%d]"括起来,那么如果输入的内容不是以0开头,它将返回[

  3. 否则,如果未将其分配给第一个接收参数,并且得到EOF或读取错误,它将返回EOF。请注意, read 错误与 matching 错误不同。读取错误是指操作系统返回尝试从磁盘读取的I / O错误。

    返回值EOF表示文件结束或I / O错误。如果您不关心区别,则可以放弃循环并继续执行该程序。但是,如果您想在ferror()上打印错误消息并将feof()视为成功,则检查ret是不够的;您必须调用这些功能之一或全部。是否执行此操作取决于您。

  

说我希望我的文件中有一堆带有4个值的记录。下面的代码能正确处理文件结尾和输入错误的情况吗?

是的。对我来说很好。


出于价值考虑,建议不要使用scanf()fscanf()。它们很复杂,使处理输入错误的难度大大超出了必要。这个问题是一个很好的示范。最好使用fgets()读取整行,然后使用sscanf()进行解析。这样一来,如果输入的内容不好,您就不会在将来的阅读工作中碰到部分麻烦了。

答案 1 :(得分:0)

我发现更容易用示例解释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);

在所有这些情况下,如果您有前导空格,则可以期待相同的行为。

// 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);