用C读取文件

时间:2011-12-15 21:07:54

标签: c parsing file-io tokenize strtok

我有一个输入文件,我需要从中提取单词。这些单词只能包含字母和数字,因此其他任何内容都将被视为分隔符。我试过fscanf,fgets + sscanf和strtok,但似乎没什么用。

while(!feof(file))
{
    fscanf(file,"%s",string);
    printf("%s\n",string);
}

上面一个显然不起作用,因为它没有使用任何分隔符所以我用这个替换了这行:

 fscanf(file,"%[A-z]",string);

它读取第一个单词,但文件指针保持倒带,所以它会一遍又一遍地读取第一个单词。

所以我使用fgets读取第一行并使用sscanf:

sscanf(line,"%[A-z]%n,word,len);
line+=len;

这个也不起作用,因为无论我尝试什么,我都无法将指针移动到正确的位置。我试过strtok,但我找不到如何设置分隔符

while(p != NULL) {
printf("%s\n", p);
p = strtok(NULL, " ");

这个显然是空白字符作为分隔符,但我确实有100个分界符。

我在这里遗漏了一些东西因为从文件中提取单词起初似乎是一个简单的概念,但我尝试的确无效吗?

4 个答案:

答案 0 :(得分:3)

考虑建立一个最小的lexer。当处于状态 word 时,只要它看到字母和数字,它就会保留在其中。遇到别的东西时会切换到状态分隔符。然后它可以在状态分隔符中完全相反。

这是一个可能有用的简单状态机的示例。为简洁起见,它仅适用于数字。 echo "2341,452(42 555" | ./main将在单独的行中打印每个数字。它不是词法分析器,但在状态之间切换的想法非常相似。

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

int main() {
  static const int WORD = 1, DELIM = 2, BUFLEN = 1024;
  int state = WORD, ptr = 0;
  char buffer[BUFLEN], *digits = "1234567890";
  while ((c = getchar()) != EOF) {
    if (strchr(digits, c)) {
      if (WORD == state) {
        buffer[ptr++] = c;
      } else {
        buffer[0] = c;
        ptr = 1;
      }
      state = WORD;
    } else {
      if (WORD == state) {
        buffer[ptr] = '\0';
        printf("%s\n", buffer);
      }
      state = DELIM;
    }
  }
  return 0;
}

如果状态数增加,您可以考虑用if块替换检查当前状态的switch语句。通过将整个输入块读取到临时缓冲区并迭代它来替换{​​{1}},可以提高性能。

如果必须处理更复杂的输入文件格式,您可以使用词法分析器生成器,例如flex。他们可以为您定义状态转换和词法生成的其他部分。

答案 1 :(得分:2)

有几点:

首先,不要使用feof(file)作为循环条件; feof之后尝试读取文件末尾之前不会返回true,因此您的循环将经常执行一次。

其次,你提到了这个:

fscanf(file,"%[A-z]",string);

它读取第一个单词,但文件指针保持倒带,所以它一遍又一遍地读取第一个单词。

这不是正在发生的事情;如果流中的下一个字符与格式说明符不匹配,则scanf返回时没有读取任何内容,并且string未被修改。

这是一个简单的,如果不优雅的方法:它从输入文件中一次读取一个字符,检查它是字母还是数字,如果是,则将其添加到字符串中。

#include <stdio.h>
#include <ctype.h>

int get_next_word(FILE *file, char *word, size_t wordSize)
{
  size_t i = 0;
  int c;

  /**
   * Skip over any non-alphanumeric characters
   */
  while ((c = fgetc(file)) != EOF && !isalnum(c))
    ; // empty loop

  if (c != EOF)
    word[i++] = c;

  /**
   * Read up to the next non-alphanumeric character and
   * store it to word
   */
  while ((c = fgetc(file)) != EOF && i < (wordSize - 1) && isalnum(c))
  {
      word[i++] = c;
  }
  word[i] = 0;
  return c != EOF;
}

int main(void)
{
   char word[SIZE]; // where SIZE is large enough to handle expected inputs
   FILE *file;
   ...
   while (get_next_word(file, word, sizeof word))
     // do something with word
   ...
}

答案 2 :(得分:1)

我会用:

FILE *file;
char string[200];

while(fscanf(file, "%*[^A-Za-z]"), fscanf(file, "%199[a-zA-Z]", string) > 0) {
    /* do something with string... */
}

这会跳过非字母,然后读取最多199个字母的字符串。唯一奇怪的是,如果你有超过199个字母的任何“单词”,它们将被分成多个单词,但你需要限制以避免缓冲区溢出......

答案 3 :(得分:0)

你的分隔符是什么? strtok的第二个参数应该是一个包含分隔符的字符串,第一个应该是第一次指向字符串的指针然后是NULL

char * p = strtok(line, ","); // assuming a , delimiter
printf("%s\n", p);

while(p)
{
    p = strtok(NULL, ",");
    printf("%S\n", p);
}