【问题标题】:Best way to read a parse data from text file in C?从 C 中的文本文件中读取解析数据的最佳方法?
【发布时间】:2012-03-29 17:55:35
【问题描述】:

我正在处理从文本文件中读取数据并将该数据解析为各种数组的任务。例如,我的文本文件的一部分如下所示:

arbrick  pts/26       141.219.210.189  Thu Mar 29 11:23 - 11:24  (00:00)    
rjmcnama pts/27       141.219.205.107  Thu Mar 29 11:02   still logged in   
ajhoekst pts/26       99.156.215.40    Thu Mar 29 10:59 - 11:08  (00:08)    
eacarter pts/31       141.219.162.145  Thu Mar 29 10:50 - 10:51  (00:00)    
kmcolema pts/31       141.219.214.128  Thu Mar 29 09:44 - 09:47  (00:03) 

我需要将数据解析为以下数组:用户 ID、终端、IP 地址和事件时间。考虑到列之间的空白数量不一致,我该怎么做?

编辑: 我尝试使用 Thiruvalluvar 提供的建议,但我无法让它发挥作用。但是,我确实切换到了 sscanf,并且效果很好几乎...

while(!feof(myfile)) {
        fgets(buffer, 256, myfile);
        sscanf(buffer, "%s %s %s %s", user_id[i], terminal_id[i], ip_addr[i], events[i]);
    } /*End while not EOF*/

有效的是 user_id、terminal_id 和 ip_addr 数组。但是,events 数组目前还不能完美运行。由于 events 数组是一个包含空格的字符串,我如何使用 sscanf 将 buffer 的剩余部分添加到 events 数组中?

【问题讨论】:

  • 令我惊讶的是,所有解决方案似乎都在使用字符串函数来搜索流。虽然这可行,但根据经验,最快的解析器使用 fgetc 一次迭代一个字符。虽然一开始这似乎违反直觉,但您必须记住,libc 仍会读取页面大小的块,并且您的代码最终会在 cpu 上运行得非常快(执行大腿循环而不是执行函数调用、mallocs、memmoves 等) .我仍然对这些好处感到好奇,因为为此您最终仍将所有内容分配给 C 数组,所以也许我会通过时间比较来尝试一下。

标签: c parsing


【解决方案1】:

我认为,问题的真正部分是如何仅将它们存储在 4 个数组中。例如:

arbrick  pts/26       141.219.210.189  Thu Mar 29 11:23 - 11:24  (00:00)    

用空格标记这一行会给出很多字符串。但我们只对将整行拆分为 4 行感兴趣,仅此而已。

解决方案:

  1. 使用fgets()读取该行。

  2. 使用strtok()strtok_r()(用于线程安全)以空格作为分隔符对其进行标记。

  3. 将前 3 个字符串读入数组:user_id、terminal_id 和 ip_address

  4. 将其余字符串存储(并附加)到数组events中。

    int i = 0;    
    int line_index = 0;     
    char *p;    
    while(...) //loop to read the file
    {
        fgets(line);
        p = strtok(line, " ");
        i=0;
    
        while(p!=NULL)
        {
    
            if(i==0) strcpy(user_id[line_index], p);
    
            if(i==1) strcpy(terminal_id[line_index], p);
    
            if(i==2) strcpy(ip_addr[line_index], p);
    
            else     strcat(events[line_index], p); //anything else goes into array events
    
            i++;
    
        }
    
        line_index++;
    } //end of file-reading loop.
    

【讨论】:

  • 好吧,我知道你打算用这个做什么了,我试了一下。但是,我们只是介绍了指针,我认为我使用它们并不完全正确。当程序到达如下行时:strcpy(user_id[line_index], p);我得到一个段错误。使用二维字符数组存储字符串数组是否更有意义?
  • 必须是 2D {array of strings} 这就是 seg fault 的原因。字符用户 ID[100][25]; //对于 100 行,确保您的用户名不超过 25 个字符的长度。同样,用户数最多为 100。否则,相应地更改它或使用 malloc() 动态分配它。其他数组也是如此。
【解决方案2】:

使用fgets 一次读取一行。使用sscanf 调用在线操作来存储信息,因为数据的格式不一致(例如,“仍然登录”)。 sscanf 将读取并丢弃格式说明符之间的任何空格。

【讨论】:

    【解决方案3】:

    试试这个:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    char** split (char* string, const char* delim) {
    char* p;
    int i = 0;
    char** array;
    
    array = malloc(strlen(string) * sizeof (char*));
    p = strtok (string, delim);
    while (p != NULL) {
        array[i] = malloc(sizeof(char) );
        array[i++] = p;
        p = strtok(NULL, delim);
    }
    return array;
    }
    
    void parseLine(char *line, char *user, char term[], char ip[], char event[]) {
    char *copy = line;
    char **array = split(copy, " ");
    
    strcpy(user, *array++);
    strcpy(term, *array++);
    strcpy(ip, *array++);
    array++;array++;array++;
    strcpy(event, *array++);
    if (strcmp(*array, "-")) {
        strcat(event, " still logged in");
    } else {
        array++;
        strcat(event, " - ");
        strcat(event, *array++);
    }
    }
    
    int main(void) {
    
    char line[2048];
    char user[64], term[64], ip[64], event[64];
    
    while (fgets(line, 2048, stdin) != NULL) {
        parseLine(line, user, term, ip, event);
        printf("[%s][%s][%s][%s]\n", user, term, ip, event);
        /* use an array to save them ... */
    }
    return 0;
    }
    

    然后: ./a.out &lt; file.txt

    【讨论】:

      【解决方案4】:

      对于它的价值,这是我的建议。按如下方式滚动您自己的字符串标记器:

      static char *string_tok(char **stringp, const char *delim)
      {
          char *tok = *stringp + strspn(*stringp, delim);
          char *end = tok + strcspn(tok, delim);
      
          if (*end) {
              *end++ = '\0';
              end += strspn(end, delim);
          }
          *stringp = end;
          return tok;
      }
      

      然后为每个标记按顺序调用它。在第三次调用string_tok 之后,缓冲区buf 保存了一个指向字符串剩余部分(事件)开始的指针。注意 buf 必须是可写的。

      static void parse(char * buf)
      {
          char * user_id = string_tok(&buf, " \t");
          char * term    = string_tok(&buf, " \t");
          char * ip      = string_tok(&buf, " \t");
          printf("user_id:  %s\n", user_id);
          printf("terminal: %s\n", term);
          printf("ip addr:  %s\n", ip);
          printf("events:   %s\n\n", buf);
      }
      

      【讨论】:

        猜你喜欢
        • 2010-09-06
        • 2014-06-04
        • 1970-01-01
        • 2011-10-27
        • 2021-08-12
        • 2011-04-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多