【问题标题】:retrieving words from a single line in a file using fgets and strtok使用 fgets 和 strtok 从文件中的单行检索单词
【发布时间】:2017-07-30 05:50:06
【问题描述】:

我有以下函数从文件中读取单词并使用 fgets 和 strtok 输出每个单词,其中文件中的单词由换行符分割:

word1
word2
word3

我正在尝试模仿文件中的单词位于仅由空格分隔的单行的功能:

word1 word2 word3

但是,我似乎只能在将 strtok 字符更改为“”并尝试读取单行时获得第一个单词。我不确定我错过了什么。

#include <string.h>
#include <malloc.h>

int readLines;
char *output[255];
char *filename = "commands.txt";

char fileRead(const char *filename, char *output[255])
{
  int count = 0;
  char input[255];
  char *line;
  FILE *file = fopen(filename, "r");

  if (file == NULL) {
    printf("Cannot open file: %s\n", filename);
  } else {
    while(count < 255 && fgets(input, sizeof(input), file)) {
      line = strtok(input, "\n");
      if (line) {
        output[count++] = strdup(line); /* Store replica */
      }
    }
    fclose(file);
  }
  return count;
}

char *strdup(const char *str)
{
  char *ret = malloc(strlen(str)+1);
  if (ret) {
    strcpy(ret, str);
  }
  return ret;
}

int main(int argc, char *argv[])
{
  readLines = fileRead(filename, output);
  /* read from array and pass into flag function */
  for (int x = 0; x < readLines; ++x) {
    printf("%s\n", output[x]);
    free(output[x]);
  }
  return 0;
}

【问题讨论】:

  • 你需要在strtok()上循环。第一次调用将提供input 作为strtok() 的第一个参数;随后的NULL
  • @JonathanLeffer 啊,对了,谢谢
  • 错误消息应该输出到stderr,而不是stdout,并且当错误来自系统函数时,应该使用perror(),它会输出包含的文本和相应的系统错误信息。通常最好的办法是在显示消息后调用exit(),而不是让程序继续运行
  • 拥有一个名称(和整体签名)与系统函数相同的本地函数是一种糟糕的编程习惯。注意strdup()是包含头文件string.h时暴露的系统函数
  • 发布的代码存在大量内存泄漏。 (或者,换句话说,代码应该总是在自己之后清理。)代码应该为通过strdup()获得的每个char数组调用free()

标签: c fgets strtok


【解决方案1】:

如果我理解您的问题——您想要分隔文件中一行中包含的所有单词(tokens),那么您使用 strtok 不正确。在您的代码中,您有line = strtok(input, "\n");,其中分隔符换行符。如果您想分隔 空格 分隔的单词,那么您还需要在分隔符中包含 space,例如char *delim = " \n";

此外,在第一次调用strtok 时,您使用缓冲区的变量名(或指向缓冲区的指针)来保存要标记的文本。对于对strtok 的所有剩余调用(例如,对于单词2, 3, 4...),您使用NULL 代替它并检查返回。

将您的示例归结为以下内容,您可以执行以下操作:

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

#define MAXC 255

int main (int argc, char **argv) {

    char buf[MAXC] = "",
        *delim = " \n";
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    if (!fgets (buf, MAXC, fp)) {  /* read one line from file */
        fprintf (stderr, "error: file read failed.\n");
        return 1;
    }

    /* tokenize line with strtok */
    for (char *p = strtok (buf, delim); p; p = strtok (NULL, delim))
        printf ("%s\n", p);

    if (fp != stdin) fclose (fp);     /* close file if not stdin */

    return 0;
}

输入文件示例

$ cat dat/strtok.dat
my dog has fleas

使用/输出示例

$ ./bin/strtokoneline <dat/strtok.dat
my
dog
has
fleas

如果我误解了,请发表评论,我很乐意提供进一步的帮助。如果您对答案有任何其他问题,请尽管提问。

如果您更喜欢将strtok 循环编写为while 循环而不是for(它更易于查看),您可以执行以下操作:

    char buf[MAXC] = "",
        *p = buf,
        *delim = " \n";
    ...
    p = strtok (buf, delim);        /* get first token (word) */

    while (p) {
        printf ("%s\n", p);
        p = strtok (NULL, delim);   /* get remaining tokens */
    }

【讨论】:

  • 谢谢大卫,你是对的,我错过了那个循环。 while 确实也更干净了,谢谢。
  • 当然,很高兴为您提供帮助。此外,请始终记住,虽然 strtokstrsepsscanf 等库工具可以帮助您解析/拆分和标记化,但没有什么是您无法仅通过 遍历指针来解析或分离的>(或一对指针)在字符串中,并在你去的时候检查每个字符。对于复杂的解析,有时它是最好的方法。 (您可以简单地搜索 "walk a pointer" 并在 SO 上找到大量示例)使用库函数,但如果它们不起作用 - 指针和 if 语句将 :) 好祝你编码顺利。
猜你喜欢
  • 2013-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多