【问题标题】:Parsing text in C在 C 中解析文本
【发布时间】:2009-09-05 21:03:47
【问题描述】:

我有一个这样的文件:

...
words 13
more words 21
even more words 4
...

(一般格式是一串非数字,然后是空格,然后是任意数量的数字和换行符)

我想解析每一行,将单词放入结构的一个字段中,将数字放入另一个字段中。现在,我正在使用一种丑陋的技巧来阅读该行,而字符不是数字,然后阅读其余部分。我相信有更清晰的方法。

【问题讨论】:

  • 请详细说明格式。单词和数字之间的分隔符是否总是一个空格(即不是两个空格,或者空格和一个制表符)?数字后面可以有空格(换行符之前)吗?单词可以包含数字吗?

标签: c parsing


【解决方案1】:

编辑:您可以使用 pNum-buf 获取字符串的字母部分的长度,并使用 strncpy() 将其复制到另一个缓冲区。请务必在目标缓冲区的末尾添加一个 '\0'。我会在 pNum++ 之前插入这段代码。

int len = pNum-buf;
strncpy(newBuf, buf, len-1);
newBuf[len] = '\0';

您可以将整行读入缓冲区,然后使用:

char *pNum;
if (pNum = strrchr(buf, ' ')) {
  pNum++;
}

获取指向数字字段的指针。

【讨论】:

  • 这就是我写的,感谢 Stack Overflow 的橙色 ajaxy 警报 :-)
  • 嘿,我通常也在警报的另一边。
  • 这行得通,但是字母部分呢?如何将其复制到最后一个空格?
【解决方案2】:
fscanf(file, "%s %d", word, &value);

这会将值直接转换为字符串和整数,并处理空格和数字格式等的变化。

编辑

哎呀,我忘了你的单词之间有空格。 在这种情况下,我会执行以下操作。 (注意它会截断'line'中的原始文本)

// Scan to find the last space in the line
char *p = line;
char *lastSpace = null;
while(*p != '\0')
{
    if (*p == ' ')
        lastSpace = p;
    p++;
}


if (lastSpace == null)
    return("parse error");

// Replace the last space in the line with a NUL
*lastSpace = '\0';

// Advance past the NUL to the first character of the number field
lastSpace++;

char *word = text;
int number = atoi(lastSpace);

您可以使用 stdlib 函数解决此问题,但上述方法可能更有效,因为您只搜索您感兴趣的字符。

【讨论】:

  • %s 只会匹配到下一个空白字符。
  • 呃,我看了这个例子,然后看了下面的格式描述,忘记了格式可以有多个空格。 (脸红!)
【解决方案3】:

您可以尝试使用strtok() 对每一行进行标记,然后检查每个标记是数字还是单词(一旦有了标记字符串,就可以进行相当简单的检查 - 只需查看标记的第一个字符)。

【讨论】:

  • 仅查看令牌的第一个字符并不是一个非常可靠的检查。我不会那么信任文件中的数据。
  • 取决于文件的来源。如果这些是应用程序生成的内部文件(或格式严格且已知的预先存在的文件),那么很可能不需要进行稳健检查。
  • 一般来说,strtok() 并不是一个特别好的处理方式。双重不在线程程序中。此外,如果所需的存储空间是“可能包含空格的字符串”加数字,则 strtok 可能会将内容分解为太多部分。
  • 我实际上稍微误读了这个问题 - 我最初以为他们想单独抓取每个单词。既然不是这样,strtok 就不太合适了。
【解决方案4】:

假设数字后面紧跟着'\n'。 您可以将每一行读取到字符缓冲区,在整行上使用 sscanf("%d") 获取数字,然后计算该数字在文本字符串末尾所占用的字符数。

【讨论】:

    【解决方案5】:

    根据您的字符串变得多么复杂,您可能需要使用 PCRE 库。至少这样你就可以编译一个 perl'ish 正则表达式来分割你的行。不过这可能有点矫枉过正。

    【讨论】:

      【解决方案6】:

      根据描述,这就是我要做的:使用 fgets() 将每一行作为单个字符串读取(确保目标缓冲区足够大),然后使用 strtok() 拆分行。要确定每个标记是单词还是数字,我会使用 strtol() 来尝试转换并检查错误情况。示例:

      #include <stdlib.h>
      #include <stdio.h>
      #include <string.h>
      
      /**
       * Read the next line from the file, splitting the tokens into 
       * multiple strings and a single integer. Assumes input lines
       * never exceed MAX_LINE_LENGTH and each individual string never
       * exceeds MAX_STR_SIZE.  Otherwise things get a little more
       * interesting.  Also assumes that the integer is the last 
       * thing on each line.  
       */
      int getNextLine(FILE *in, char (*strs)[MAX_STR_SIZE], int *numStrings, int *value)
      {
        char buffer[MAX_LINE_LENGTH];
        int rval = 1;
        if (fgets(buffer, buffer, sizeof buffer))
        {
          char *token = strtok(buffer, " ");
          *numStrings = 0;
          while (token) 
          {
            char *chk;
            *value = (int) strtol(token, &chk, 10);
            if (*chk != 0 && *chk != '\n')
            {
              strcpy(strs[(*numStrings)++], token);
            }
            token = strtok(NULL, " ");
          }
        }
        else
        {
          /** 
           * fgets() hit either EOF or error; either way return 0
           */
          rval = 0;
        }
        return rval;
      }
      /**
       * sample main
       */
      int main(void)
      {
        FILE *input;
        char strings[MAX_NUM_STRINGS][MAX_STRING_LENGTH];
        int numStrings;
        int value;
      
        input = fopen("datafile.txt", "r");
        if (input)
        {
          while (getNextLine(input, &strings, &numStrings, &value))
          {
            /**
             * Do something with strings and value here
             */
          }
          fclose(input);
        }
        return 0;
      }
      

      【讨论】:

        【解决方案7】:

        鉴于描述,我想我会使用这个(现已测试)C99 代码的变体:

        #include <stdio.h>
        #include <string.h>
        #include <stdlib.h>
        #include <ctype.h>
        
        struct word_number
        {
            char word[128];
            long number;
        };
        
        int read_word_number(FILE *fp, struct word_number *wnp)
        {
            char buffer[140];
            if (fgets(buffer, sizeof(buffer), fp) == 0)
                return EOF;
            size_t len = strlen(buffer);
            if (buffer[len-1] != '\n')  // Error if line too long to fit
                return EOF;
            buffer[--len] = '\0';
            char *num = &buffer[len-1];
            while (num > buffer && !isspace(*num))
                num--;
            if (num == buffer)         // No space in input data
                return EOF;
            char *end;
            wnp->number = strtol(num+1, &end, 0);
            if (*end != '\0')  // Invalid number as last word on line
                return EOF;
            *num = '\0';
            if (num - buffer >= sizeof(wnp->word))  // Non-number part too long
                return EOF;
            memcpy(wnp->word, buffer, num - buffer);
            return(0);
        }
        
        int main(void)
        {
            struct word_number wn;
            while (read_word_number(stdin, &wn) != EOF)
                printf("Word <<%s>> Number %ld\n", wn.word, wn.number);
            return(0);
        }
        

        您可以通过为不同的问题返回不同的值来改进错误报告。 您可以使其与行的单词部分的动态分配内存一起使用。 你可以让它使用比我允许的更长的行。 您可以向后扫描数字而不是非空格 - 但这允许用户编写“abc 0x123”并且正确处理十六进制值。 您可能更愿意确保单词部分没有数字;此代码无关紧要。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-01-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-21
          相关资源
          最近更新 更多