Более быстрый ввод / вывод в C - PullRequest
7 голосов
/ 01 апреля 2009

У меня есть проблема, которая будет принимать 1000000 строк ввода, как показано ниже, из консоли.

0 1 23 4 5
1 3 5 2 56
12 2 3 33 5
...
...

Я использовал scanf, но он работает очень медленно. Есть ли способ быстрее получить информацию с консоли? Я мог бы использовать read (), но я не уверен насчет количества байтов в каждой строке, поэтому я не могу как read () читать 'n' байтов. Спасибо, Очень обязан

Ответы [ 8 ]

6 голосов
/ 01 апреля 2009

Используйте fgets (...), чтобы вывести строку за раз. Обратите внимание, что вы должны проверить '\ n' в конце строки, и если его нет, вы либо в EOF, либо вам нужно прочитать значение другого буфера и объединить их вместе. Вспенить, промыть, повторить. Не попадайтесь с переполнением буфера.

ПОТОМ, вы можете самостоятельно проанализировать каждую логическую строку в памяти. Мне нравится использовать strspn (...) и strcspn (...) для такого рода вещей, но ваш пробег может отличаться.

Синтаксический: Определите строку разделителей. Используйте strspn () для подсчета символов «не данных», которые соответствуют разделителям, и пропустите их. Используйте strcspn () для подсчета символов "data", которые НЕ соответствуют разделителям. Если этот счетчик равен 0, все готово (в строке больше нет данных). В противном случае скопируйте эти N символов для передачи в функцию синтаксического анализа, такую ​​как atoi (...) или sscanf (...). Затем сбросьте базу указателей до конца этого фрагмента и повторите процесс пропуска разделителей, копирования данных, преобразования в числовые значения.

3 голосов
/ 01 апреля 2009

Если ваш пример показателен, что у вас действительно есть фиксированный формат из пяти десятичных чисел на строку, я бы, вероятно, использовал комбинацию fgets() для чтения строк, а затем цикл, вызывающий strtol() для преобразования из строки в целое число.

Это должно быть быстрее, чем scanf(), но при этом все же более понятно и более высокоуровнево, чем самостоятельное преобразование строки в целое.

Примерно так:

typedef struct {
  int number[5];
} LineOfNumbers;

int getNumbers(FILE *in, LineOfNumbers *line)
{
  char buf[128];  /* Should be large enough. */
  if(fgets(buf, sizeof buf, in) != NULL)
  {
    int i;
    char *ptr, *eptr;

    ptr = buf;
    for(i = 0; i < sizeof line->number / sizeof *line->number; i++)
    {
      line->number[i] = (int) strtol(ptr, &eptr, 10);
      if(eptr == ptr)
        return 0;
      ptr = eptr;
    }
    return 1;
  }
  return 0;
}

Примечание: это непроверенный (даже не скомпилированный!) Код, написанный браузером. Но, возможно, полезно в качестве конкретного примера.

3 голосов
/ 01 апреля 2009

Вы используете несколько read с буфером фиксированного размера, пока не достигнете конца файла.

2 голосов
/ 01 апреля 2009

Используйте бинарный ввод / вывод, если можете. Преобразование текста может замедлить чтение в несколько раз . Если вы используете текстовый ввод-вывод, потому что его легко отлаживать, рассмотрите еще раз двоичный формат и используйте программу od (при условии, что вы работаете в Unix), чтобы сделать ее удобочитаемой при необходимости.

О, еще одна вещь: есть библиотека SFIO от AT & T, которая обозначает более безопасный и быстрый ввод-вывод файлов. Возможно, вам также повезет с этим, но я сомневаюсь, что вы получите такое же ускорение, как и в двоичном формате.

1 голос
/ 01 апреля 2009

Чтение строки за раз (если буфер недостаточно велик для строки, разверните и продолжите с большим буфером).

Затем используйте для преобразования специальные функции (например, atoi), а не общие.

Но, прежде всего, установите повторяемый тестовый жгут с профилированием, чтобы изменения действительно ускоряли процесс.

0 голосов
/ 06 апреля 2012

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

Вот код:

#include <iostream>

int maxio=1000000;
char buf[maxio], *s = buf + maxio;

inline char getc1(void)
{
   if(s >= buf + maxio) { fread_unlocked(buf,sizeof(char),maxio,stdin); s = buf; }
   return *(s++);
}
inline int input()
{
   char t = getc1();
   int n=1,res=0;
   while(t!='-' && !isdigit(t)) t=getc1(); if(t=='-')
   {
      n=-1; t=getc1();
   }
   while(isdigit(t))
   {
     res = 10*res + (t&15);
     t=getc1();
   }
   return res*n;
}

Это реализовано в C++. В C вам не нужно включать iostream, функция isdigit() неявно доступна.

Вы можете принять ввод как поток символов, вызвав getc1(), и получить целочисленный ввод, вызвав input().

Вся идея использования fread() состоит в том, чтобы взять все данные сразу. Вызов scanf()/printf() многократно отнимает драгоценное время при блокировке и разблокировке потоков, что полностью избыточно в однопоточной программе.

Также убедитесь, что значение maxio таково, что весь ввод может быть получен только за несколько «круговых поездок» (в данном случае, в идеале, один) Настройте его по мере необходимости.

Надеюсь, это поможет!

0 голосов
/ 02 апреля 2009

fread все равно вернется, если вы попытаетесь прочитать больше байтов, чем есть.

Я нашел один из самых быстрых способов чтения файлов:

/ * искать конец файла * / FSEEK (файл, 0, SEEK_END);

/ * получить размер файла * / размер = ftell (файл);

/ * искать начало файла * / FSEEK (файл, 0, SEEK_SET);

/ * сделать буфер для файла * / буфер = malloc (1048576);

/ * зачитывать по 1 МБ за раз, пока вы не достигнете размера байтов и т. Д.

На современных компьютерах включите свой ОЗУ и загрузите все это в ОЗУ, тогда вы легко сможете пробираться через память.

По крайней мере вы должны использовать fread с размерами блоков настолько большими, насколько это возможно, и, по крайней мере, такими же большими, как блоки кеша или размер сектора жесткого диска (минимум 4096 байт, я бы лично использовал минимум 1048576). Вы обнаружите, что с гораздо большими требованиями к чтению rfead может последовательно получать большой поток за одну операцию. Предлагаемое здесь некоторыми людьми использование 128 байтов нелепо ... так как в конечном итоге накопителю придется постоянно искать, так как крошечная задержка между вызовами приведет к тому, что голова уже пройдет следующий сектор, который почти наверняка имеет последовательные данные, которые вы хотите.

0 голосов
/ 01 апреля 2009

Из любопытства, что генерирует так много строк, которые быстро в консоли?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...