Каков наиболее эффективный способ распечатать файл в C на стандартный вывод? - PullRequest
4 голосов
/ 27 июня 2010

Я застрял на этом. В настоящее время я использую:

FILE *a = fopen("sample.txt", "r");
int n;
while ((n = fgetc(a)) != EOF) {
  putchar(n);
}

Однако этот метод кажется немного неэффективным. Есть ли лучший способ? Я пытался использовать fgets:

char *s;
fgets(s, 600, a);
puts(s);

Есть одна вещь, которую я нахожу неправильной в этом втором методе, это то, что вам потребуется действительно большое число для второго аргумента fgets.

Спасибо за все предложения. Я нашел способ (кто-то в IRC сказал мне это), используя open (), read () и write ().

char *filename = "sample.txt";
char buf[8192];
int r = -1;
int in = open(filename, O_RDONLY), out = 0;
if (in == -1)
  return -1;
while (1) {
  r = read(in, buf, sizeof(buf));
  if (r == -1 || r == 0) { break; }
  r = write(out, buf, r);
  if (r == -1 || r == 0) { break; }
}

Ответы [ 5 ]

9 голосов
/ 27 июня 2010

Второй код не работает.Вам нужно выделить буфер, например:

char s[4096];
fgets(s, sizeof(s), a);

Конечно, это не решит вашу проблему.

Считайте куски фиксированного размера из входных данных и запишите все, что будет прочитано в:

int n;
char s[65536];
while ((n = fread(s, 1, sizeof(s), a))) {
    fwrite(s, 1, n, stdout);
}

Возможно, вы также захотите проверить ferror(a), если он остановился по какой-то другой причине, кроме достижения EOF.

Примечания

  1. Я изначально использовалбуфер 4096 байт, потому что это довольно распространенный размер страницы для выделения памяти и размер блока для файловой системы.Тем не менее, сладкое место в моей системе Linux, похоже, находится на отметке 64 кБ, что меня удивило.Возможно, здесь важен кеш процессора, но я просто догадываюсь.
  2. Для холодного кеша это практически не имеет значения, так как пейджинг ввода / вывода будет доминировать;даже один байт за раз работает примерно с одинаковой скоростью.
5 голосов
/ 27 июня 2010

Самый эффективный метод будет сильно зависеть от операционной системы. Например, в Linux вы можете использовать sendfile:

struct stat buf;
int fd = open(filename, O_RDONLY);
fstat(fd, &buf);
sendfile(0, fd, NULL, buf.st_size);

Это делает копирование непосредственно в ядре, сводя к минимуму ненужные копии из памяти в память. Другие платформы могут иметь аналогичные подходы, такие как write() использование stdout из mmap буфера ed.

1 голос
/ 27 июня 2010

Я полагаю, что FILE, возвращаемое fopen, типично (всегда?) Буферизуется, поэтому ваш первый пример не так неэффективен, как вы думаете.

Второй может работать немного лучше ... если вы исправите ошибки: не забудьте выделить буфер и помните, что ставит добавляет новую строку!.

Другой вариант - использовать двоичные операции чтения (fread).

0 голосов
/ 27 июня 2010

То, что вы делаете, достаточно хорошо в 99% приложений. Конечно, в большинстве библиотек C stdio работает плохо, и вам будет лучше с Phong Vo's sfio library . Если у вас есть измерения , показывающие, что это узкое место, следующий естественный шаг - выделить буфер и использовать fread / fwrite. Вы не хотите fgets, потому что вас не волнуют переводы строк.


Сначала заставь это бежать, затем сделай это правильно. Вы, вероятно, не должны делать это быстро.

0 голосов
/ 27 июня 2010

Все зависит от того, что вы хотите сделать с данными.

это приведет к сбою:

char *s;
fgets(s, 600, a);
puts(s);

, поскольку s не является буфером, просто где-то указатель.

Одним из способов является чтение всего файла в буфер и работа с ним с помощью fread ()

filebuffer = malloc(filelength);
fread( buffer, 1, filelength, fp );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...