Как всё-таки победить fscanf при чтении чисел из текстового файла :)
Из текстового файла требуется прочитать в динамический массив произвольное количество чисел, например, вещественных.
Студенты, да и не только они, традиционно используют fscanf
, хотя у этой функции, как у "простой" scanf
, куча подводных камней.
Например, она "сбивается" после данных, не соответствующих шаблону.
Но для извлечения из текстового файла числовых значений нужного типа и записи их в динамический массив функцию всё же можно приспособить.
Проще всего, видимо, сделать это за 2 прохода по файлу - на первом проходе подсчитать, сколько в файле чисел, затем выделить память под динамический массив, встать на начало файла и повторить чтение уже известного объёма данных.
Задачи "подсчитать в текстовом файле количество значений заданного формата" и "переписать значения, заданные в нужном формате, в массив" похожи, так что функция для них будет одна и та же -
int read_float_numbers (FILE *fp, bool onlycount, float *a);
fp - дескриптор открытого файла;
onlycount - если true, только считает количество чисел, иначе пишет их в a;
a - место, куда писать (память должна быть выделена).
Тестировать такой код имеет смысл на всех типовых ситуациях:
конец последнего числа есть последний символ файла:
100 200
после последнего числа есть разделители (перевод строки, пробелы):
100 200
файл заканчивается "мусором" без перевода строки:
100 200 sdsds
файл заканчивается "мусором" с переводом строки:
100 200 sdsds
в файле есть значения и "мусор" вперемешку:
a=100,b=200
файл вообще пуст или состоит только из мусора:
q
файл содержит "числа" типа 1e1000 или 9999999999999999999999999999:
1e3 1.5 1e1000 9999999999999999999999999999,h=13.6 22
Ну и т.д. :)
Вот примерно такой код выходит (проверен в Visual Studio):
#define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <cstdlib> #include <cmath> int read_float_numbers(FILE *fp, bool onlycount, float *a) { float f; char c; int cnt = 0; fseek(fp, 0, SEEK_SET); while (1) { int i = fscanf(fp, "%f", &f); if (i && isfinite(f)) { if (!onlycount) a[cnt] = f; cnt++; } else fscanf(fp, "%c", &c); if (feof(fp)) break; } return cnt; } int main() { FILE *fp = fopen("data.txt", "rt"); if (!fp) { printf("Can't open data.txt from current folder!"); getchar(); exit(1); } int cnt = read_float_numbers(fp, true, NULL); if (!cnt) { printf("No digits in file data.txt!"); getchar(); exit(2); } float *a = new float[cnt]; if (!a) { printf("No memory for %d items!", cnt); getchar(); exit(3); } read_float_numbers(fp, false, a); for (int i = 0; i < cnt; i++) printf("%f ", a[i]); getchar(); return 0; }
Возможно, надёжный способ также - прочитать все данные в строковый буфер и потом его проанализировать. :)
P.S. Предполагается, что текстовый файл с именем data.txt
находится в "текущей папке текущего проекта", например, если у Вас Windows 7, имя решения и проекта = Console, имя юзера = ПК, версия Studio = 2010, а конфигурация решения = Debug, это будет
C:\Users\ПК\Мои документы\Visual Studio 2010\Projects\Console\Console\
21.10.2016, 15:52 [6514 просмотров]