【问题标题】:C programming, getting the last line of fileC编程,获取文件最后一行
【发布时间】:2013-12-14 14:59:31
【问题描述】:

我正在编写一个打开 txt 文件并想读取 txt 文件的最后一行的 c 程序。 我不太精通 C,所以请记住,我可能不知道 C 中的所有概念。我被困在使用 fscanf 读取 txt 文件的所有行的部分,但我想读最后一行txt 文件并获取如下所述的值。

这是我不完整的代码:

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

FILE *sync;


void check()
{
    int success; //to hold the results if the timestamps match
    sync = fopen("database.txt","r");
    char file[] = "database.txt";

    while (fscanf(sync, "%d.%06d", &file) != EOF)
    {


    }

    fclose(sync);
}

示例 txt 文件:

/////// / //// ///// ///// //////////////// Time: 1385144574.787665 //////// /
/////// / //// ///// ///// //////////////// Time: 1385144574.787727 //////// /
/////// / //// ///// ///// //////////////// Time: 1385144574.787738 //////// /
/////// / //// ///// ///// //////////////// Time: 1385144574.787746 //////// /
/////// / //// ///// ///// //////////////// Time: 1385144574.787753 //////// /

/是一些我不想要的单词、符号和数字,只是上面示例txt中的数字

感谢任何示例并指出我犯的错误,以便我能更好地理解这一点。 由于我让一些人对文本文件感到困惑,这就是它的真正含义。这是它的格式,所以我应该知道每行的长度。但是,我将无法知道会有多少行,因为它可能会更新。

 Socket: 0 PGN: 65308 Data: 381f008300000000 Time: 1385144574.787925 Address: 28
 Socket: 0 PGN: 65398 Data: 0000000100000000 Time: 1385144574.787932 Address: 118
 Socket: 0 PGN: 61444 Data: f07d83351f00ffff Time: 1385144574.787940 Address: 4
 Socket: 0 PGN: 65266 Data: 260000000000ffff Time: 1385144574.787947 Address: 242
 Socket: 0 PGN: 65309 Data: 2600494678fff33c Time: 1385144574.787956 Address: 29
 Socket: 0 PGN: 65398 Data: 0000000100000000 Time: 1385144574.787963 Address: 118
 Socket: 0 PGN: 61444 Data: f07d833d1f00ffff Time: 1385144574.787971 Address: 4
 Socket: 0 PGN: 65398 Data: 0000000100000000 Time: 1385144574.787978 Address: 118
 Socket: 0 PGN: 61443 Data: d1000600ffffffff Time: 1385144574.787985 Address: 3
 Socket: 0 PGN: 65308 Data: 451f008300000000 Time: 1385144574.787993 Address: 28
 Socket: 0 PGN: 65317 Data: e703000000000000 Time: 1385144574.788001 Address: 37

我再次关注 txt 文件最后一行的时间值(例如 1385144574.787925)。 希望这会有所帮助。

【问题讨论】:

  • 为了帮助您“更好地理解它”,首先需要知道您为什么这样做。通过将带有"database.txt"file 字符串传递给fscanf,您想做什么?这样做的目的是什么?
  • 你问了一个有两大部分的问题:1,如何只看最后一行(Elias 的回答很好)。 2、如何从该行中提取时间元素。如果不知道您所说的 / 是一些我不想要的单词、符号和数字 的确切含义,就无法确切知道如何为您提供帮助。单词、符号和 数字 可以包含任何 ascii 字符。如果要排除除 Time: 1385144574.787665 等之外的所有其他输入。等,那么您将不得不使用strtok()(或strtok_r())解析该行以排除所有不需要的数据。
  • 如果您提供一些输入行的完整示例,那么您可能会得到一个完整的解决方案。在发表此评论时,回答者不可能完全解决这个问题。

标签: c file-handling


【解决方案1】:

由于您在文件的最后一行之后,并且您没有提及文件可能有多大,因此可能值得从最后开始读取文件,然后从那里向后工作:

FILE *fp = fopen("database.txt", "r");
fseek(fp, 0, SEEK_END);//sets fp to the very end of your file

从那里,您可以使用fseek(fp, -x, SEEK_CUR);,其中 x 是您想要返回的字节数,直到您到达您想要的位置……除此之外,Jekyll 的答案应该可以正常工作。
但是,为了得到最后一行,我倾向于这样做:

FILE *fp = fopen("database.txt", "r");
char line[1024] = "";
char c;
int len = 0;
if (fp == NULL) exit (EXIT_FAILURE);
fseek(fp, -1, SEEK_END);//next to last char, last is EOF
c = fgetc(fp);
while(c == '\n')//define macro EOL
{
    fseek(fp, -2, SEEK_CUR);
    c = fgetc(fp);
}
while(c != '\n')
{
    fseek(fp, -2, SEEK_CUR);
    ++len;
    c = fgetc(fp);
}
fseek(fp, 1, SEEK_CUR);
if (fgets(line, len, fp) != NULL) puts(line);
else printf("Error\n");
fclose(fp);

len var 背后的原因是我可以分配足够的内存来容纳整行。使用 1024 个字符的数组就足够了,但如果您想安全使用:

char *line = NULL;
//read line
line = calloc(len+1, sizeof(char));
if (line == NULL)
{
    fclose(fp);
    exit( EXIT_FAILURE);
}
//add:
free(line);//this line!
fclose(fp);

一旦你得到那一行,你就可以使用 Jekyll 的 sscanf 示例来确定从那一行中提取你想要的任何内容的最佳方法。

【讨论】:

  • 文件可能包含超过一千行,当我更新它时可能会增加
  • 好吧,在这种情况下,您绝对应该向后阅读文件。考虑到您只对文件的最后一行感兴趣...
  • @EliasVanOotegem 我给了你 +1,因为我一直使用它并且我喜欢它。顺便说一句,我没有提出这个建议,因为我最近一次尝试有人抱怨“根据标准”寻找多个职位是不安全的(即使它总是在 Linux/Unix 中工作)。你知道吗?我在任何地方都找不到这个广告。
  • @Jekyll: fseek((void *), int, SEEK_END) 在二进制流上使用时会导致未定义的行为。这不是这里的情况,there's a pretty good summary here。如果您添加一个 包括读取整个文件的示例(就像您的 while (fgets()) 现在所做的那样,我很乐意支持您的答案,顺便说一句......到目前为止两者的更完整,更全面的答案
  • @EliasVanOotegem 感谢您的总结。所以这是真的仅适用于二进制 流。这是有道理的。
【解决方案2】:

您使用 fscanf 的方式是错误的,因为实际的参数向量需要与您收集的内容相匹配(如您在手册页中所见)。除了使用 fscanf,您可以考虑使用 fgets,然后通过 sscanf 使用正则表达式过滤您在最新原始文件中寻找的内容。

注意:: 我采集的是double格式的值,你可以根据你的问题选择最适合你的格式(string?int.int?float? ),为此,您应该检查 regex using scanf。如果您无法完成此任务,请回来。

更新:: 由于一些请求,我写了一些不同模式匹配的示例。这些应该是解决问题的良好起点。

更新:: 1. 我看到您添加了 db 文件的模式,因此我们现在可以声明 #3 和 #4 都匹配并将 3 放在这里(更快)。 2. 根据您的要求,我删除了 feof 检查,但请注意,如果您知道自己在做什么,检查就可以了 。基本上你必须记住 stream 的内部位置指示器可能会指向下一个操作的文件结尾,但是,文件结尾指示器可能不会设置,直到操作尝试读取那一点。 3.您要求删除字符行[1024]={0,};该指令用于初始化 line[1024] 数组,该数组将包含您从文件中读取的行。这是需要的!要了解该指令是什么,请参阅here

代码:

void check()
{
   char line[1024]={0,}; // Initialize memory! You have to do this (as for your question)
   int n2=0;
   int n3=0;
   sync = fopen("database.txt", "r");
   if( sync ) {
      while( fgets(line, 1024, sync) !=NULL ) {
      // Just search for the latest line, do nothing in the loop
      } 
      printf("Last line %s\n", line); //<this is just a log... you can remove it
      fclose(sync);
      // This will look for Time and it will discard it collecting the number you are looking for in n2 and n3
      if (sscanf(line, "%*[^T]Time: %d.%d", &n2, &n3) ) {
          printf( "%d.%d\n", n2, n3);
      }
   }
}

示例 2
例如,如果您需要使用两个整数来收集值,则需要将上面示例的 sscanf 替换为以下代码:

  unsigned int n2, n3;
  if (sscanf(line, "%*[^0-9]%d.%d", &n2, &n3) ) {
    printf( "%d.%d\n", n2, n3);
  }

说这个你应该弄清楚如何收集其他格式。

示例 3 一个更好的正则表达式。如果在给定模式之前文件中还有其他数字,您可能希望在 Time 上匹配,所以假设之前没有任何 T。一个正则表达式可以是:

 if (sscanf(line, "%*[^T]Time: %d.%d", &n2, &n3) ) {
    printf( "%d.%d\n", n2, n3);
}

使用 sscanf 的正则表达式可能不适合您的模式,在这种情况下,您需要考虑使用 gnu regex library 或者您可以像我在以下示例中那样混合使用 strstr 和 sscanf。

示例 4 如果您没有找到共同的模式,这可能很有用。在这种情况下,您可能希望在调用 sscanf 之前使用 strstr 触发字符串“时间”

  char *ptr = strstr( line, "Time:" );
  if( ptr != NULL ) {
     if (sscanf(ptr, "%*[^0-9]%d.%d", &n2, &n3) ) {
        printf( "%d.%d\n", n2, n3);
     }
  }

* 注意 * 您可能需要找到解析文件的方法,而上述内容可能只是建议,因为您的文件中可能有更具体或不同的模式,但我在此处发布的说明应该足以为您提供完成这项工作的工具案例

【讨论】:

  • 根据 cmets,OP 想要执行 timestamps 的匹配。应该是准确的。您尝试使用 double 会扭曲值并阻止精确计算。
  • @AndreyT 我认为如果这是他需要的,他应该玩 sscanf 并收集他正在寻找的正确形式(也许是一个字符串,也许是一个 int.int?)。我怀疑作为指南,这个 sn-p 应该足够了。你同意吗?
  • 嗯,%*[0-9] 是 OP 学习的好东西。但是,看起来有人已经帮助 OP 使用 %d.%d 方法。在没有任何解释的情况下丢弃它可能会使 OP 感到困惑。
  • @AndreyT 好的,我同意你的观点,我还会为 int.int 添加 sn-p 以便我可以放弃这篇文章
  • 这看起来像是未定义的行为。如果文件为空,则 line[] 在 printf 中未初始化。 使用while(!feof(fp)) 的可怕帕斯卡病几乎总是一个错误
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-26
  • 2017-03-09
  • 1970-01-01
相关资源
最近更新 更多