Visual C++: почему fscanf не читает числа в цикле
Решал человек в Visual Studio (C++) типовую задачку по форматному чтению данных из текстового файла. Например:
Файл text.txt в текущей папке приложения имеет следующий вид: 1 1.5 -3.5 2 3.5 Прочитать его как последовательность вещественных чисел.
Вот программа:
#define _CRT_SECURE_NO_WARNINGS #include <windows.h> #include <locale.h> #include <stdio.h> #include <stdlib.h> int main(void) { setlocale(LC_ALL,"Rus"); SetConsoleCP(1251); SetConsoleOutputCP(1251); FILE *fp = fopen ("text.txt","r"); if (fp==NULL) { printf ("\nне удалось открыть файл"); getchar(); exit (1); } float a; while (1) { fscanf (fp,"%f",&a); if (feof(fp)) break; //Если файл кончился, выйти из цикла //здесь выполняется обработка очередного значения a, например: printf ("%.2f ",a); } fclose(fp); fflush(stdin); getchar(); return 0; }
Увы, при её запуске в консоли бесконечно "крутится" только прочитанная первой "единичка", будто и не работает цикл while
.
В чём причина? Да она не одна. На самом деле, к такой программке требуется сделать минимум 5 пояснений:
1. Функции семейства scanf
возвращают целое число - количество значений, которые успешно прочитаны в соответствии с указанным форматом. В реальных приложениях эту величину следует проверять в коде:
int i=fscanf (fp,"%f",&a); if (i!=1) { //не удалось получить 1 значение }
2. На "восприятие" программой данных может влиять установленная в приложении локаль. Например, если до показанного кода выполнен оператор
setlocale(LC_ALL,"Rus");
результат работы кода может измениться (для русской локали разделителем целой и дробной части числа является запятая, а не точка).
3. Очередное чтение данных изменяет внутренний файловый указатель. Этот указатель в любой момент времени, пока файл открыт, показывает на следующее значение, которое будет прочитано. Благодаря этому наш код с "бесконечным" while
не зациклился.
4. Код показывает, как читать из файла заранее неизвестное количество значений – это позволяет сделать стандартная функция feof
(проверка, достигнут ли конец файла; вернёт не 0
, если прочитано всё).
5. Распространённый в примерах из Сети код вида
while (!feof(fp)) { fscanf (fp,"%f",&a); //обработка числа a }
в ряде компиляторов может породить неточности при интерпретации данных. Например, этот код может прочитать как последнее значение завершающий перевод строки в файле, благодаря чему последнее прочитанное значение "удвоится".
Итак, убираем или комментируем
setlocale(LC_ALL,"Rus");
и всё заработало :)
15.10.2015, 15:55 [8009 просмотров]