【问题标题】:How can I read lines from bottom up using C?如何使用 C 从下到上读取行?
【发布时间】:2015-07-05 05:48:42
【问题描述】:

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

文件是这样的:

4.32
5.32
1.234
0.123
9.3
6.56
8.77

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

8.77
6.56
9.3

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

【问题讨论】:

标签: c file-read


【解决方案1】:

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

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

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]

【讨论】:

  • −1 永远不要使用scanf 做任何事情。
  • 我知道将scanf%s 一起使用的危险。您是否有任何理由相信将scanf%f 一起使用存在危险?
  • 是的:正如我在回答中所说,输入溢出会触发未定义的行为。
  • 我对这个主题的咆哮在这里:stackoverflow.com/questions/24302160/scanf-on-an-istream-object/…(阅读所有的 cmets!)该标准的违规句子是 C99 7.19.6.2p10:“如果 [the object that will receive the转换的结果] 没有适当的类型,或者如果转换的结果不能在对象中表示,则行为未定义。"强调我的。
  • @GrijeshChauhan,感谢您的链接。我更新了解决scanf问题的答案。
【解决方案2】:

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

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

【讨论】:

  • 这会读取整个文件以确定大小并寻找并读取后半部分。这是一种“两全其美”的方法,而且您根本无法从额外的复杂性中获得任何好处。我建议您恢复使用first approach here,因为它不仅更简单,而且速度更快。
  • 有一天,您会想从管道/套接字中读取数据。不幸的是,它们没有尺寸,无法在其中寻找。
  • @Joker_vD,问题很明确:“我需要一个使用 fseek 处理文件指针位置的解决方案”
【解决方案3】:

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

【讨论】:

  • 如果文件非常大并且不需要顶部条目,则不实用。
  • @JoshuaByer 实际上你是对的,但我想知道解决这个问题的最佳方法。该配方将在内存中创建不必要的空间。我说的对吗?
  • @ErolGuzoğlu 你真的不需要阅读它们,你可以数一数然后重新启动并跳到倒数第三个。
【解决方案4】:

首先,打开文件:

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];

【讨论】:

    【解决方案5】:

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

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

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

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

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

    附:如果您有 shell 实用程序 tac(这是一个 GNUism),最简单的选择是编写程序来处理标准输入上的 前三个数字,然后将其调用为 @ 987654336@。 Skimming the code 让我相信 tac 实现了我的“第三种方法”,并带有一些额外的聪明之处。

    【讨论】:

    • 您介意扩展(或提供相关链接)scanf() 的未定义行为吗?我尝试在 SO 和 Google 上搜索,但找不到任何内容。
    • @ace 我的罐头咆哮在这里:stackoverflow.com/questions/24302160/scanf-on-an-istream-object/…(阅读所有的 cmets!)标准的违规句子是 C99 7.19.6.2p10:“如果 [the object that will接收转换结果] 没有适当的类型,或者如果转换结果不能在对象中表示,则行为未定义。"强调我的。
    • tac?不是tail -n 3
    • @kay OP 确实说他们想要数字倒序,但你的想法也很好(可以自己倒序)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-22
    • 1970-01-01
    • 1970-01-01
    • 2014-12-12
    • 1970-01-01
    • 2011-06-08
    • 1970-01-01
    相关资源
    最近更新 更多