ZetCode

C strtok_s 函数

最后修改:2025 年 4 月 8 日

字符串标记(tokenization)是 C 语言编程中的一项常见操作,而 `strtok_s` 函数是用于分割字符串的 `strtok` 函数的更安全版本。本教程将深入探讨 `strtok_s`,包括其语法、用法以及相对于 `strtok` 的优势。我们将通过实际示例进行探讨,并讨论为何在现代 C 编程中应优先使用 `strtok_s`。理解 `strtok_s` 有助于编写更安全、更可靠的字符串处理代码。

什么是 strtok_s?

`strtok_s` 函数是用于将字符串分割成标记(tokens)的 `strtok` 函数的一个更安全替代方案。它是 C11 标准的 Annex K 边界检查接口的一部分。`strtok_s` 添加了一个上下文参数(context parameter)以在调用之间维护状态,使其成为线程安全的。它还执行运行时约束检查。与 `strtok` 不同,它可以检测无效参数和缓冲区溢出。在安全性至关重要的新代码中,请始终优先使用 `strtok_s`。

strtok_s 的基本用法

本示例演示了使用 `strtok_s` 进行基本的字符串标记。

basic_tokenize.c
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "apple,banana,cherry";
    char *token;
    char *context = NULL;
    const char *delim = ",";

    // First call to strtok_s
    token = strtok_s(str, delim, &context);

    while (token != NULL) {
        printf("Token: %s\n", token);
        // Subsequent calls with NULL as first argument
        token = strtok_s(NULL, delim, &context);
    }

    return 0;
}

此代码将逗号分隔的字符串分割成单独的标记。`strtok_s` 将其状态保存在 `context` 指针中。第一次调用使用要标记的字符串,后续调用使用 NULL。每次调用都会返回指向下一个标记的指针,或在完成后返回 NULL。与 `strtok` 相比,上下文参数使其成为线程安全的。

使用多个分隔符进行标记

`strtok_s` 可以处理多个分隔符字符,如下所示。

multi_delim.c
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "apple;banana cherry,orange";
    char *token;
    char *context = NULL;
    const char *delim = "; ,";

    token = strtok_s(str, delim, &context);

    while (token != NULL) {
        printf("Fruit: %s\n", token);
        token = strtok_s(NULL, delim, &context);
    }

    return 0;
}

本示例使用多个分隔符(分号、空格和逗号)对字符串进行标记。`strtok_s` 将这些字符的任何序列视为单个分隔符。输出显示所有水果都已分隔,无论使用了哪种分隔符。这种灵活性使 `strtok_s` 对于解析复杂输入非常有用。始终确保您的分隔符字符串包含所有可能的分隔符。

处理空标记

本示例演示了 `strtok_s` 如何处理连续的分隔符。

empty_tokens.c
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "apple,,banana,,,cherry";
    char *token;
    char *context = NULL;
    const char *delim = ",";
    int count = 0;

    token = strtok_s(str, delim, &context);

    while (token != NULL) {
        printf("Token %d: '%s'\n", ++count, token);
        token = strtok_s(NULL, delim, &context);
    }

    printf("Total tokens: %d\n", count);
    return 0;
}

当出现连续分隔符时,`strtok_s` 会跳过它们之间的空标记。此示例中有多个连续的逗号,但只找到三个非空标记。如果您需要保留空标记,请考虑使用 `strsep` 或手动解析等替代方法。上下文参数即使在复杂的分隔符模式下也能确保正确的状态跟踪。

在嵌套循环中进行标记

本示例展示了如何在嵌套标记中使用 `strtok_s`。

nested_tokenize.c
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <string.h>

int main() {
    char data[] = "name=John;age=30;city=New York";
    char *outer_token;
    char *outer_context = NULL;
    char *inner_token;
    char *inner_context = NULL;
    const char *outer_delim = ";";
    const char *inner_delim = "=";

    outer_token = strtok_s(data, outer_delim, &outer_context);

    while (outer_token != NULL) {
        printf("Pair: %s\n", outer_token);
        
        inner_token = strtok_s(outer_token, inner_delim, &inner_context);
        printf("  Key: %s\n", inner_token);
        inner_token = strtok_s(NULL, inner_delim, &inner_context);
        printf("  Value: %s\n", inner_token);

        outer_token = strtok_s(NULL, outer_delim, &outer_context);
    }

    return 0;
}

此代码首先按分号分割,然后按等号分割,演示了嵌套标记。每个级别都使用自己的上下文变量,从而实现了安全的嵌套操作。外部循环分割键值对,而内部循环则分离键和值。这种模式在配置文件解析中很常见。独立的上下文变量可防止标记级别之间的干扰。

使用 strtok_s 进行错误处理

本示例展示了如何对 `strtok_s` 进行适当的错误处理。

error_handling.c
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <string.h>
#include <errno.h>

int main() {
    char *str = NULL;  // Invalid input
    char *token;
    char *context = NULL;
    const char *delim = ",";

    token = strtok_s(str, delim, &context);

    if (token == NULL && errno != 0) {
        perror("strtok_s failed");
        return 1;
    }

    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok_s(NULL, delim, &context);
    }

    return 0;
}

`strtok_s` 在遇到无效参数时会设置 `errno`。本示例演示了在调用失败后检查错误。当传入 NULL 字符串指针时,`strtok_s` 返回 NULL 并将 `errno` 设置为 EINVAL。为了实现健壮的错误处理,请务必在每次调用后同时检查返回值和 `errno`。这是其相对于 `strtok` 的一个关键优势,因为 `strtok` 提供了任何错误报告机制。

使用 strtok_s 的最佳实践

来源

C strtok_s 文档

本教程涵盖了 `strtok_s` 函数,从基本用法到高级场景。作为 `strtok` 的更安全替代方案,它应成为您在现代 C 编程中进行字符串标记的首选。

作者

我的名字是 Jan Bodnar,我是一名专注的程序员,对编码充满热情。自 2007 年以来,我通过 1,400 多篇文章和 8 本电子书分享了我的专业知识。凭借十多年的教学经验,我致力于让编程变得易于理解且引人入胜。

列表 C 标准库