在C中向后读取文本文件

Jos*_*hun 13 c text stream standard-library

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

abc
def
ghi

应该读行ghi,def,abc.

到目前为止,我尝试过:

    #include <stdio.h>
    #include <stdlib.h>

    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;


    }
Run Code Online (Sandbox Code Playgroud)

试图简单地读取一行并缓冲它.对不起,我的代码很糟糕,我很困惑.我知道你通常会为整个文件分配内存然后读入数据,但是对于经常更改的大文件我认为最好直接读取(特别是如果我想在文件中搜索文本).

提前致谢

*对不起忘了提到这将在Linux上使用,所以换行只是没有CR的NL.*

Joh*_*nck 10

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

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


Ale*_*nze 7

我建议使用更便携(希望)的文件大小确定方式,因为fseek(binaryStream, offset, SEEK_END)不能保证工作.请参阅下面的代码.

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

#include <limits.h>
#include <string.h>
#include <stdio.h>

/* 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;
}
Run Code Online (Sandbox Code Playgroud)

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