使用 strtok 获取行中的最后一个词

Taking of the last word in the line with strtok

给定一个包含以下行的文件:

word1 word2 word3 word4

我试着写了下面的代码:

FILE* f = fopen("my_file.txt", "r");
char line[MAX_BUFFER + 1];
if (fgets(line, MAX_LENGTH_LINE, f) == NULL) {
    return NULL;
}
char* word = strtok(line, " ");
for (int i = 0; i < 4; i++) {
    printf("%s ", word);
    word = strtok(NULL, " ");
}

用于打印 "words"。

它正在工作。但是,我有些不明白。

最后一个词是如何实现的word4? (我不明白,因为在 "word4" 之后不存在 space)..

我不太确定你在问什么。您是在问程序如何能够从文件中正确读取 word4,即使它后面没有跟 space?或者你问为什么,当程序打印 word4 退出时,它似乎没有在它之后打印 space?

第一个问题的答案是 strtok 旨在为您提供标记 分隔符分隔,而不是 由 [=50= 终止] 分隔符。不要求最后一个标记后跟定界符。

看到第二个问题的答案,如果我们稍微调整程序和打印输出可能会更清楚:

char* word = strtok(line, " ");
for (int i = 0; word != NULL; i++) {
    printf("%d: \"%s\"\n", i, word);
    word = strtok(NULL, " ");
}

我在这里做了两处修改:

  1. 循环运行s直到word为NULL,即只要strtok再找一个词就行了。 (这是为了确保我们看到 所有 个单词,并确保我们不会试图以任何方式特殊对待第四个单词。如果您 试图以某种方式特殊对待第四个词,请这样说。)
  2. 这些单词用引号括起来打印出来,这样我们就可以准确地看到它们包含的内容。

当我运行修改程序时,我看到:

0: "word1"
1: "word2"
2: "word3"
3: "word4
"

最后一行乍一看很奇怪,但解释很简单。您最初使用 fgets 读取该行,它会将终止 \n 字符复制到 line 缓冲区中。所以它最终停留在 word4 上;即第四个"word"是"word4\n".

因此,将 \n 包含在您交给 strtok 的一组白色 space 定界字符中通常是个好主意——也就是说,您可以调用 strtok(line, " \n") 代替。如果我这样做(在两个 strtok 调用中),输出变为

0: "word1"
1: "word2"
2: "word3"
3: "word4"

这可能更接近您的预期。

您的代码不检查 strtok() 的 return 值,在某些情况下可能不安全。

/*  Split string
    @content origin string content
    @delim   delimiter for splitting
    @psize   pointer pointing at the variable to store token size
    @return  tokens after splitting
 */
const char **split(char *content, const char *delim, int *psize)
{
    char *token;
    const char **tokens;
    int capacity;
    int size = 0;

    token = strtok(content, delim);
    if (!token)
    {
        return NULL;
    }

    // Initialize tokens
    tokens = malloc(sizeof(char *) * 64);
    if (!tokens)
    {
        exit(-1);
    }
    capacity = 64;

    tokens[size++] = token;

    while ((token = strtok(NULL, delim)))
    {
        if (size >= capacity)
        {
            tokens = realloc(tokens, sizeof(char *) * capacity * 2);
            if (!tokens)
            {
                exit(-1);
            }
            capacity *= 2;
        }
        tokens[size++] = token;
    }

    // if (size < capacity)
    // {
    //  tokens = realloc(tokens, sizeof(char *) * size);
    //  if (!tokens)
    //  {
    //      exit(-1);
    //  }
    // }
    *psize = size;

    return tokens;
}