БлогNot. Как всё-таки победить fscanf при чтении чисел из текстового файла :)

Как всё-таки победить 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 [6357 просмотров]


теги: c++ учебное числа форматы studio

К этой статье пока нет комментариев, Ваш будет первым