在C中向后读取文本文件

在C中向后读取文件的最佳方法是什么? 我知道一开始你可能会认为这没什么用处,但是大多数日志等都会在文件末尾添加最新的数据。 我想从文件向后读取文本,将其缓冲为行 – 即

ABC
高清
GHI

应该读行ghidefabc

到目前为止,我尝试过:

#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上使用,所以换行只是没有CR的NL。 *

我推荐一种更便携(希望)的文件大小确定方式,因为fseek(binaryStream, offset, SEEK_END)不能保证工作。 请参阅下面的代码。

我认为文件应该至少在内核级别进行最低限度的缓冲(例如,默认情况下每个文件至少缓冲一个块),因此搜索不应该产生大量的额外I / O,并且只应在内部提升文件位置。 如果默认缓冲不满意,您可以尝试使用setvbuf()来加速I / O.

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

我只在带有2个不同编译器的Windows上进行了测试。

你可以通过程序tac管道输入,这就像cat但向后!

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

有很多方法可以做到这一点,但一次读取一个字节肯定是较差的选择之一。

读取最后一个,比如4KB,然后从最后一个字符向上走回到上一个换行符将是我的选择。

另一种选择是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 

(剪辑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 

这不是太长,有点超过600行,虽然它包含一些高级function,并使用其他来源的function,反向线路缓冲实现似乎在那个tac.c源文件中。

每个字节的FSEEKing听起来很慢。

如果你有内存,只需将整个文件读入内存,然后将其反转或向后扫描。

另一种选择是Windows内存映射文件。