Чтение текстового файла назад в C

Каков наилучший способ чтения файла в C? Я знаю, что сначала вы можете подумать, что это бесполезно, но большинство журналов и т. Д. Добавляют самые последние данные в конце файла. Я хочу прочитать текст из файла назад, буферизируя его в строки – это

азбука
Защита
ГХИ

должен читать ghi , def , abc в строках.

До сих пор я пробовал:

#include  #include  void read_file(FILE *fileptr) { char currentchar = '\0'; int size = 0; while( currentchar != '\n' ) { currentchar = fgetc(fileptr); printf("%c\n", currentchar); fseek(fileptr, -2, SEEK_CUR); if( currentchar == '\n') { fseek(fileptr, -2, SEEK_CUR); break; } else size++; } char buffer[size]; fread(buffer, 1, size, fileptr); printf("Length: %d chars\n", size); printf("Buffer: %s\n", buffer); } int main(int argc, char *argv[]) { if( argc < 2) { printf("Usage: backwards [filename]\n"); return 1; } FILE *fileptr = fopen(argv[1], "rb"); if( fileptr == NULL ) { perror("Error:"); return 1; } fseek(fileptr, -1, SEEK_END); /* Seek to END of the file just before EOF */ read_file(fileptr); return 0; } 

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

заранее спасибо

* Извините забыл упомянуть, что это будет использоваться в Linux, поэтому новые строки – это просто NL без CR. *

Я рекомендую более переносимый (надеюсь) способ определения размера файла, поскольку fseek(binaryStream, offset, SEEK_END) не гарантированно работает. См. Код ниже.

Я считаю, что файлы должны быть, по крайней мере, минимально буферизованы на уровне ядра (например, буферизация по меньшей мере одного блока на файл по умолчанию), поэтому запросы не должны вызывать значительный объем дополнительных операций ввода-вывода и должны только внутренне продвигать положение файла. Если буферизация по умолчанию не является удовлетворительной, вы можете попытаться использовать setvbuf() для ускорения ввода-вывода.

 #include  #include  #include  /* File must be open with 'b' in the mode parameter to fopen() */ long fsize(FILE* binaryStream) { long ofs, ofs2; int result; if (fseek(binaryStream, 0, SEEK_SET) != 0 || fgetc(binaryStream) == EOF) return 0; ofs = 1; while ((result = fseek(binaryStream, ofs, SEEK_SET)) == 0 && (result = (fgetc(binaryStream) == EOF)) == 0 && ofs <= LONG_MAX / 4 + 1) ofs *= 2; /* If the last seek failed, back up to the last successfully seekable offset */ if (result != 0) ofs /= 2; for (ofs2 = ofs / 2; ofs2 != 0; ofs2 /= 2) if (fseek(binaryStream, ofs + ofs2, SEEK_SET) == 0 && fgetc(binaryStream) != EOF) ofs += ofs2; /* Return -1 for files longer than LONG_MAX */ if (ofs == LONG_MAX) return -1; return ofs + 1; } /* File must be open with 'b' in the mode parameter to fopen() */ /* Set file position to size of file before reading last line of file */ char* fgetsr(char* buf, int n, FILE* binaryStream) { long fpos; int cpos; int first = 1; if (n <= 1 || (fpos = ftell(binaryStream)) == -1 || fpos == 0) return NULL; cpos = n - 1; buf[cpos] = '\0'; for (;;) { int c; if (fseek(binaryStream, --fpos, SEEK_SET) != 0 || (c = fgetc(binaryStream)) == EOF) return NULL; if (c == '\n' && first == 0) /* accept at most one '\n' */ break; first = 0; if (c != '\r') /* ignore DOS/Windows '\r' */ { unsigned char ch = c; if (cpos == 0) { memmove(buf + 1, buf, n - 2); ++cpos; } memcpy(buf + --cpos, &ch, 1); } if (fpos == 0) { fseek(binaryStream, 0, SEEK_SET); break; } } memmove(buf, buf + cpos, n - cpos); return buf; } int main(int argc, char* argv[]) { FILE* f; long sz; if (argc < 2) { printf("filename parameter required\n"); return -1; } if ((f = fopen(argv[1], "rb")) == NULL) { printf("failed to open file \'%s\'\n", argv[1]); return -1; } sz = fsize(f); // printf("file size: %ld\n", sz); if (sz > 0) { char buf[256]; fseek(f, sz, SEEK_SET); while (fgetsr(buf, sizeof(buf), f) != NULL) printf("%s", buf); } fclose(f); return 0; } 

Я тестировал это только на windowsх с двумя разными компиляторами.

Вы можете просто пропустить вход через программу tac , которая похожа на cat но назад!

http://linux.die.net/man/1/tac

Существует несколько способов сделать это, но чтение байта за один раз, безусловно, является одним из худших вариантов.

Чтение последнего, скажем, 4 КБ, а затем переход от последнего символа к предыдущей новой строке было бы моим выбором.

Другой вариант – mmap -файл и просто притворяться, что файл представляет собой кусок памяти, и отсканируйте его назад. [Вы можете указать mmap вы тоже читаете назад, чтобы сделать для вас предварительную выборку данных].

Если файл ОЧЕНЬ большой (несколько гигабайт), вы можете использовать только небольшую часть файла в mmap .

Если вы хотите узнать, как это сделать, вот пример Debian / Ubuntu (для других, таких как дистрибутивы на основе RPM, при необходимости адаптируйте):

 ~$ which tac /usr/bin/tac ~$ dpkg -S /usr/bin/tac coreutils: /usr/bin/tac ~$ mkdir srcs ~$ cd srcs ~/srcs$ apt-get source coreutils 

(clip apt-get output)

 ~/srcs$ ls coreutils-8.13 coreutils_8.13-3.2ubuntu2.1.diff.gz coreutils_8.13-3.2ubuntu2.1.dsc coreutils_8.13.orig.tar.gz ~/srcs$ cd coreutils-8.13/ ~/srcs/coreutils-8.13$ find . -name tac.c ./src/tac.c ~/srcs/coreutils-8.13$ less src/tac.c 

Это не слишком долго, чуть более 600 строк, и, хотя он содержит некоторые дополнительные функции и использует функции из других источников, реализация буферизации обратной линии, похоже, находится в этом исходном файле tac.c

FSEEKing для каждого байтового звука PAINFULLY медленно.

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

Другой вариант – файлы с отображением в памяти Windows.

Давайте будем гением компьютера.