Leer un archivo de texto al revés en C

¿Cuál es la mejor manera de leer un archivo al revés en C? Sé que al principio puede estar pensando que esto no sirve para nada, pero la mayoría de los registros, etc. añaden los datos más recientes al final del archivo. Quiero leer el texto del archivo al revés, almacenarlo en líneas, eso es

a B C
def
ghi

debería leer ghi , def , abc en líneas.

Hasta ahora lo he intentado:

#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; } 

En un bash de simplemente leer una línea y almacenarla. Lamento que mi código sea terrible, me estoy confundiendo tanto. Sé que normalmente asignaría memoria para todo el archivo y luego leería los datos, pero para archivos grandes que cambian constantemente pensé que sería mejor leer directamente (especialmente si quiero buscar texto en un archivo).

Gracias por adelantado

* Lo siento, olvidé mencionar que esto se usará en Linux, por lo que las nuevas líneas son solo NL sin CR. *

Recomiendo una forma más portátil (con suerte) de determinación del tamaño del archivo, ya que no se garantiza que fseek(binaryStream, offset, SEEK_END) funcione. Vea el código a continuación.

Creo que los archivos deben estar al menos mínimamente almacenados en el kernel (por ejemplo, almacenar al menos un bloque por archivo de forma predeterminada), por lo que no se debe incurrir en cantidades significativas de E / S adicionales y solo se debe avanzar la posición del archivo internamente. Si el almacenamiento en búfer predeterminado no es satisfactorio, puede intentar usar setvbuf() para acelerar la E / S.

 #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; } 

Solo he probado esto en Windows con 2 comstackdores diferentes.

Podrías canalizar la entrada a través del progtwig tac , que es como un cat pero al revés.

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

Hay muchas formas en que puede hacer esto, pero leer un byte a la vez es definitivamente una de las opciones más pobres.

Leer la última, por ejemplo, 4 KB y luego retroceder desde el último personaje hasta la nueva línea previa, sería mi elección.

Otra opción es mmap el archivo, y simplemente pretender que el archivo es un trozo de memoria, y escanear hacia atrás en eso. [Usted puede decirle a mmap que está leyendo hacia atrás también, para hacer que capte datos por usted].

Si el archivo es MUY grande (varios gigabytes), puede usar solo una pequeña porción del archivo en mmap .

Si desea aprender cómo hacerlo, aquí hay un ejemplo de Debian / Ubuntu (para otras distribuciones basadas en RPM, adaptarse según sea necesario):

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

(clip de salida apt-get)

 ~/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 

Eso no es demasiado largo, un poco más de 600 líneas, y aunque contiene algunas características avanzadas, y usa funciones de otras fonts, la implementación del buffering de línea inversa parece estar en ese archivo fuente tac.c

FSEEKing para cada byte suena DOLOROSAMENTE lento.

Si tiene la memoria, simplemente lea todo el archivo en la memoria y revéselo o escanee hacia atrás.

Otra opción sería archivos mapeados de memoria de Windows.