C编程,获取最后一行文件

时间:2013-11-28 14:15:37

标签: c file-handling

我正在编写一个打开txt文件的c程序,想要读取txt文件的最后一行。 我不是那么精通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)之后。 希望这会有所帮助。

2 个答案:

答案 0 :(得分:6)

由于你是在文件的最后一行之后,而你没有提到文件的大小,所以从最后开始阅读文件可能是值得的,并从那里开始向后工作:

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示例来确定从该行中提取所需内容的最佳方法。

答案 1 :(得分:4)

您使用fscanf的方式是错误的,因为参数的实际向量需要与您收集的内容相匹配(如您在联机帮助页中所示)。您可以考虑使用fgets然后使用正则表达式通过sscanf过滤最新原始内容,而不是使用fscanf。

注意 ::我以双格式收集了值,您可以选择最适合您的格式(字符串?int.int?float? ),为此,您应该使用scanf 检查 正则表达式。如果你不能完成这项任务,请回来。

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

<强> 更新 ::  1.我已经看到你添加了你的db文件的模式,所以我们现在可以说#3和#4匹配并将3放在这里(更快)。  2.我已根据您的请求删除 feof 检查,但请注意 如果您知道自己在做什么,检查就没那么 。基本上你必须记住,流的内部位置指示器可能指向下一个操作的文件结尾,但是,在操作尝试读取之前,可能不会设置文件结束指示符。那一点 。  3.您要求删除char行[1024] = {0,};该指令用于初始化行[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 一个更好的正则表达式。如果在给定模式之前文件中有其他编号,您可能希望在时间上匹配,所以假设之前没有任何T。正则表达式可以是:

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

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

示例4 如果找不到常见模式,这可能很有用。在这种情况下,您可能希望在调用strstr

之前使用sscanf在字符串“Time”上触发
  char *ptr = strstr( line, "Time:" );
  if( ptr != NULL ) {
     if (sscanf(ptr, "%*[^0-9]%d.%d", &n2, &n3) ) {
        printf( "%d.%d\n", n2, n3);
     }
  }

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