fscanf: возможна ли ошибка или EOF без возврата EOF? - PullRequest
1 голос
/ 15 июня 2019

У меня есть программа C ++, которая читает из файла, который, как я ожидаю, будет иметь кучу одинаково отформатированных записей.Я хочу прекратить чтение, если я столкнулся с чем-то неожиданным, означает ли это плохо отформатированную запись или какой-то сбой ввода, и я хочу провести различие между этими различными условиями.

Я видел этот ответ и посмотрел документацию fscanf() , и я не уверен, что fscanf() может указывать на ошибку или EOF без возврата EOF.Из моего понимания обеих этих ссылок возможно, что произойдет ошибка или EOF, даже если fscanf() вернет значение 0 или больше, поэтому мне придется вызывать 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 ]

3 голосов
/ 15 июня 2019

Возможна ли ошибка или EOF без возврата EOF?

Да, это так. Вы также можете получить возвращаемое значение между 0 и 3. cplusplus.com несколько небезызвестно. Давайте взглянем на страницу cppreference.com .

Возвращаемое значение: число успешно назначенных принимающих аргументов (которое может быть равно нулю в случае, если сбой сопоставления произошел до того, как был назначен первый получающий аргумент), или EOF, если сбой ввода произошел до того, как был назначен первый получающий аргумент.

Есть несколько разных сценариев. Давайте разберем это на случаи:

  1. Если он успешно назначен первому полученному аргументу , тогда вы получите положительное значение, гарантированное. Допустим, он назначен двум переменным и затем нажимает EOF Он вернет 2 и feof() вернет true.

  2. В противном случае, если он не назначен первому получающему аргументу и получит соответствующий сбой , он вернет 0. Что такое ошибка? Вот когда он соответствует спецификатору, например %d, и не получает правильное целое число. Если ввод foobar, тогда %d не будет соответствовать.

    Или, реже, это когда он ищет буквальный символ и не видит его. Например, если ваша строка формата ожидает, что каждое число будет заключено в квадратные скобки ("[%d] [%d] [%d] [%d]"), то она вернет 0, если ввод не начинается с [.

  3. В противном случае, если он не назначен первому полученному аргументу и получит EOF или ошибку чтения , он вернет EOF. Обратите внимание, что ошибка read отличается от ошибки match . Ошибка чтения - это когда ОС возвращает ошибку ввода-вывода при попытке чтения с диска.

    Возвращаемое значение EOF указывает либо на конец файла, либо на ошибку ввода / вывода. Если вас не волнует различие, вы можете просто отказаться от цикла и продолжить работу с программой. Однако, если вы хотите напечатать сообщение об ошибке на ferror() и трактовать feof() как успешный, проверки ret недостаточно; Вы должны вызвать одну или обе из этих функций. Вам решать, хотите вы это или нет.

Скажем, я ожидаю, что в моем файле будет несколько записей с 4 значениями. Будет ли приведенный ниже код правильно обрабатывать любой конец файла и вводить условия ошибки?

Да. Выглядит хорошо для меня.


Для чего это стоит, я рекомендую не использовать scanf() и fscanf(). Они сложны и делают обработку ошибок ввода значительно труднее, чем необходимо. Этот вопрос является отличной демонстрацией. Лучше использовать fgets() для чтения всей строки и sscanf() для ее анализа. Таким образом, при неправильном вводе у вас не будет частичной строки, зацикливающейся на будущих чтениях.

0 голосов
/ 16 июня 2019

Мне проще объяснить возвращаемое значение семейства функций 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);
...