如何使用 C 从下往上读取行?

How can I read lines from bottom up using C?

我需要从下往上读取文件中列出的数字。我如何使用 C 来做到这一点?

文件如下:

4.32
5.32
1.234
0.123
9.3
6.56
8.77

比如我想读最后三个数字。它们必须是 float 类型。

8.77
6.56
9.3

PS.: 实际上我需要一个使用 fseek 等来处理文件指针位置的解决方案

重要的是要了解现代操作系统不会跟踪文件中换行符的位置。 (VMS 可以,而且我很确定某些 IBM 大型机操作系统也可以,但您可能没有使用其中任何一个。)因此不可能寻找到线边界。也无法逆序逐字节读取。

因此,逆向读取文件中最后三个数字的最简单方法是正向读取整个文件,保留最近看到的三个数字在缓冲区中。当您遇到 EOF 时,只需向后处理该缓冲区。

一种更有效但更复杂的技术是猜测接近但在文件中最后三个数字之前的位置;寻找那个位置,然后丢弃字符直到你遇到换行符;并从那时起使用上一段中的技术。如果您猜错了并且缓冲区中的数字少于三个,请再猜一次。

第三种方法是使用 fseek(使用 SEEK_END)和 fread 来读取最后 1024 个左右的 字节的文件,设置一个指向块末尾的指针,并向后解析它。这会非常有效,但会比之前的建议更令人头疼的极端情况。 (如果文件的最后三行加起来超过 1024 字节,你会怎么做?)

仅供参考,在 C 中读取浮点数的正确方法是使用 fgets and strtod。不要为此使用 atofscanfatof 不会告诉您语法错误,scanf 会在溢出时触发未定义的行为。

P.S。如果你有 shell 实用程序 tac (which is a GNUism), the easiest option of all would be to write your program to process the first three numbers on standard input, and then invoke it as tac < input.file | ./a.out. Skimming the code 让我相信 tac 实现了我的 "third approach",还有一些额外的聪明。

嗯,显而易见的方法是将它们全部读取,将它们放入一个数组中,然后获取最后三个。

不存在从文件中向后读取的概念。

一种解决方案是读取所有数字并仅存储最后读取的三个。

float numbers[3];
char line[100]; // Make it large enough
int = 0;
char* end;
for ( ; ; ++i )
{
    i %= 3; // Make it modulo 3.
    if ( fgets(line, 100, stdin) == NULL )
    {
       // No more input.
       break;
    }

    float n = strtof(line, &end);
    if ( line == end )
    {
       // Problem converting the string to a float.
       // Deal with error
       break;
    }

    if ( errno == ERANGE )
    {
       // Problem converting the string to a float within range.
       // Deal with error
       break;
    }

    numbers[i] = n;
}

如果文件中至少有三个数字,则最后三个数字为numbers[i]numbers[(i+2)%3]numbes[(i+1)%3]

我用下面的代码解决了我的问题。我读了文件的后半部分。

  FILE *fp = fopen("sample.txt","r");

  if( fp == NULL )
  {
    perror("Error while opening the file.\n");
    exit(EXIT_FAILURE);
  }

  int size=0;
  char ch;

  //Count lines of file
  while(( ch = fgetc(fp) ) != EOF )
  {
    if (ch=='\n') { size++; }
  }

  int i;
  float value;

  //Move the pointer to the end of the file and calculate the size of the file.
  fseek(fp, 0, SEEK_END);
  int size_of_file = ftell(fp);

  for (i=1; i<=size/2; i++)
  {
    //Set pointer to previous line for each i value.
    fseek(fp, (size_of_file-1)-i*5, SEEK_SET);
    fscanf(fp, "%f", &value);
  }

首先,打开文件:

FILE* fp = fopen(..., "r");

然后,跳到 EOF:

fseek(fp, 0, SEEK_END);

现在,返回 X 行:

int l = X, ofs = 1;
while (l && fseek(fp, ofs++, SEEK_END) == 0) {
    if (fgetc(fp) == '\n' && ofs > 2) l--;
}

最后,从当前位置读取X个数字:

float numbers[X];
for(int p = 0; p < X; p++) fscanf(fp, "%f", &numbers[p];