ZetCode

C strtok 函数

最后修改:2025 年 4 月 8 日

字符串操作是 C 编程的基础,而 strtok 是将字符串拆分为标记的关键函数。本教程将深入介绍 strtok,包括其语法、用法和潜在的陷阱。我们将探讨实际示例,并讨论更安全的替代方法,如 strtok_s。理解 strtok 有助于在保持程序安全的同时解析和处理字符串数据。

什么是 strtok?

strtok 函数使用指定的定界符将字符串分解为标记。它在 string.h 中声明,并通过将定界符替换为 null 字符来修改原始字符串。strtok 不是线程安全的,并在调用之间维护内部状态。对于安全关键代码,请考虑 strtok_sstrtok_r,它们提供边界检查和线程安全。始终小心修改字符串。

基本的 strtok 用法

此示例演示了使用 strtok 进行基本的字符串标记化。

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

int main() {
    char str[] = "apple,orange,banana";
    char *token;

    // Get first token
    token = strtok(str, ",");
    
    // Get remaining tokens
    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok(NULL, ",");
    }

    return 0;
}

在此,strtok 在每个逗号定界符处拆分字符串。第一次调用使用字符串指针,而后续调用使用 NULL。该函数返回指向每个标记的指针。请注意,strtok 会修改原始字符串。这是解析逗号分隔值或类似定界数据的一种简单方法。

使用 strtok 进行多定界符

strtok 可以处理多个定界符字符,如下所示。

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

int main() {
    char str[] = "apple orange,banana;pear";
    char *token;

    // Use space, comma, and semicolon as delimiters
    token = strtok(str, " ,;");
    
    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok(NULL, " ,;");
    }

    return 0;
}

此示例使用多个定界符(空格、逗号、分号)来拆分字符串。定界符字符串包含所有应分隔标记的字符。strtok 将这些字符的任何序列视为单个定界符。这种灵活性使其可用于解析各种文本格式。请记住,连续的定界符被视为一个。

安全替代方案:strtok_s

此示例演示了 C11 中提供的更安全的 strtok_s 函数。

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

int main() {
    char str[] = "one:two:three";
    char *token;
    char *context;

    // Safe tokenization with context pointer
    token = strtok_s(str, ":", &context);
    
    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok_s(NULL, ":", &context);
    }

    return 0;
}

strtok_s 通过使用显式上下文指针而不是内部状态来添加线程安全性。上下文指针跟踪标记化进度。此函数推荐用于多线程应用程序。虽然并非普遍可用,但它包含在 C11 的可选附录 K 中。宏 __STDC_WANT_LIB_EXT1__ 启用这些更安全的功能。

使用不同定界符进行标记化

此示例显示如何在 strtok 调用之间更改定界符。

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

int main() {
    char str[] = "name=John Doe;age=30;city=New York";
    char *token;

    // First split by semicolon
    token = strtok(str, ";");
    
    while (token != NULL) {
        printf("Field: %s\n", token);
        
        // For each field, split by equals
        char *key = strtok(token, "=");
        char *value = strtok(NULL, "=");
        
        printf("  Key: %s, Value: %s\n", key, value);
        token = strtok(NULL, ";");
    }

    return 0;
}

此代码首先按分号拆分字符串,然后按等号拆分每个生成的标记。可以通过使用不同的定界符来实现嵌套标记化。但是,这种方法可能会令人困惑并可能导致错误。对于复杂的解析,请考虑使用专用解析库或编写自定义解析器。始终清晰地记录此类嵌套标记化。

逐行标记化文件

此示例演示了如何逐行读取文件并标记化每一行。

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

int main() {
    FILE *file = fopen("data.txt", "r");
    if (file == NULL) {
        perror("Error opening file");
        return 1;
    }

    char line[256];
    while (fgets(line, sizeof(line), file) {
        // Remove newline character
        line[strcspn(line, "\n")] = '\0';
        
        char *token = strtok(line, ",");
        while (token != NULL) {
            printf("Token: %s\n", token);
            token = strtok(NULL, ",");
        }
        printf("----\n");
    }

    fclose(file);
    return 0;
}

此程序逐行读取文件,并使用逗号标记化每一行。fgets 通过缓冲区大小检查安全地读取每一行。在标记化之前删除换行符。此模式对于处理 CSV 文件或其他基于行的格式很有用。请记住,始终检查文件操作是否存在错误并妥善关闭文件。

使用 strtok 的最佳实践

来源

C strtok 文档

本教程探讨了 strtok 函数,从基本用法到高级注意事项。虽然它在字符串解析方面功能强大,但始终要小心使用它,以防止程序中的安全问题和未定义行为。

作者

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

列表 C 标准库