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

Помощь дата->рейтинг Поиск Почта RSS канал Статистика nickolay.info Домой

Как всё-таки победить 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 <cstring>
#include <float.h>

int read_float_numbers (FILE *fp, bool onlycount, float *a) {
 float f=FLT_MAX; char c; int cnt = 0;
 fseek(fp, 0, SEEK_SET);
 while (1) {
  int i=fscanf(fp, "%f", &f); 
  if (i && f<FLT_MAX) { //вообще-то, следовало f!=FLT_MAX ?
   if (!onlycount) a[cnt] = f;
   cnt++;
  }
  else fscanf(fp, "%c", &c); 
  f = FLT_MAX;
  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;
}

Самый непростой момент - как раз со "слишком большими числами". Например, 1e1000 останется для fscanf "числом", хотя и вызовет в Studio "превращение" числа в значение 1.#INFOOO. Нам просто повезло, что для .NET оно "меньше" FLT_MAX (см. комментарий, изначально в коде стоял знак "не равно").

Как ни странно, сам по себе C/C++ встроенных кроссплатформенных средств обнаружения такого переполнения не имеет. Единственный реально надёжный способ - прочитать все данные в строковый буфер и потом его проанализровать. А не scanf'ы :)

P.S. Предполагается, что текстовый файл с именем data.txt находится в "текущей папке текущего проекта", например, если у Вас Windows 7, имя решения и проекта = Console, имя юзера = ПК, версия Studio = 2010, а конфигурация решения = Debug, это будет

C:\Users\ПК\Мои документы\Visual Studio 2010\Projects\Console\Console\


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

21.10.2016, 15:52; рейтинг: 2931

  свежие записипоиск по блогукомментариистатистика

Наверх Яндекс.Метрика
© PerS
вход