通过在没有 strtok 的情况下标记字符串来学习 C

Learning C by Tokenizing a String without strtok

我正在通过查看斯坦福大学和其他大学的作业来学习 C(我不是任何地方的学生)。

其中一项作业是实现更简单的 strtok,但我无法正确完成。这是我目前所拥有的:

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

int tokenize(const char **input, const char *delimmter, char buf[], int buf_size)
{
    int i = strcspn(*input, delimmter);
    strncpy(buf, *input, i > buf_size ? buf_size : i);
    *input += i+1;

    if (i == strlen(*input))
        return 0;

    return 1;
}

int main(int argc, char *argv[])
{
    const char *input = "super-duper-awesome-magnificent";
    char buf[11];
    while (tokenize(&input, "-", buf, sizeof(buf)))
    {
        printf("Next Token : %s\n", buf);
    }

    return 0;
}

Next Token : super
Next Token : duper
Next Token : awesome
Next Token : magnificent
Next Token : Next Token

如果我正确理解指针和内存 (stack/heap),那么我的实现中的错误包括:

1) It's incorrect: It prints all the tokens followed by the phrase "Next Token" because that's the next piece of memory in the program. It only stops because there's nothing left on the stack

2) I am not really using buf_size correctly. Any token longer than 11 characters will cause problems.

但我现在的目标只是解决第一个问题,即如何终止while循环。

当你这样做时:

int i = strcspn(*input, delimmter);

i 将具有完全由不在 delimmter。如果 delimmter 中的字符在 *input 中,i 将更短 比 strlen(*input).

strncpy(buf, *input, i > buf_size ? buf_size : i);

这一行在buf中最多复制i > buf_size ? buf_size : i个字符, 假设 i 大于 buf_size,这意味着您将复制 buf_size buf 中的元素,但不会以“[=30=]”结尾,因为 strncpy 不会 如果未找到,则写入 '[=32=]'-终止符。所以必须设置 '[=32=]'终止字节。

strncpy(buf, *input, i > buf_size ? buf_size : i);
buf[(i >= buf_size ? buf_size - 1: i)] = 0;

如果i小于buf_size,那么位置i就是 '[=32=]'-终止字节应该去。如果 i 大于或等于 buf_size 或等于, 然后 buf_size-1 是缓冲区中的最后一个位置,这就是 '[=32=]'-终止字节应该去。

这个

*input += i+1;

并非对所有情况都是正确的。如果在 *input 中找不到分隔符,则 i 将与 strlen(*input) 相同。在那种情况下,你想要 input 点 到 '[=32=]' 终止字节而不是超过它,因为您将访问 下一次迭代内存越界。如果i小于长度, 那么增量就可以了。所以,正确的版本应该是

*input += i + (i != strlen(*input));

这个

if (i == strlen(*input))
    return 0;

应该删除。在这种情况下,整个 *input 字符串必须是 returned 和函数应该 return 1. 这就是为什么你可以删除它。但 您必须测试的是 *input 是否为空字符串。在这种情况下,所有 令牌已被 returned,你应该 return 0。在 strcspn呼唤。

所以

int tokenize(const char **input, const char *delimmter, char buf[], int buf_size)
{
    if(**input == 0)
        return 0;

    int i = strcspn(*input, delimmter);
    strncpy(buf, *input, i > buf_size ? buf_size : i);
    buf[(i > buf_size ? buf_size - 1: i)] = 0;

    *input += i + (i != strlen(*input));

    return 1;
}

这会给你想要的结果。如果您更改 main 函数 buffchar buf[3] 的声明,这将是输出:

Next Token : su
Next Token : du
Next Token : aw
Next Token : ma

哪个是正确的。