C strtok 函数
最后修改:2025 年 4 月 8 日
字符串操作是 C 编程的基础,而 strtok 是将字符串拆分为标记的关键函数。本教程将深入介绍 strtok,包括其语法、用法和潜在的陷阱。我们将探讨实际示例,并讨论更安全的替代方法,如 strtok_s。理解 strtok 有助于在保持程序安全的同时解析和处理字符串数据。
什么是 strtok?
strtok 函数使用指定的定界符将字符串分解为标记。它在 string.h 中声明,并通过将定界符替换为 null 字符来修改原始字符串。strtok 不是线程安全的,并在调用之间维护内部状态。对于安全关键代码,请考虑 strtok_s 或 strtok_r,它们提供边界检查和线程安全。始终小心修改字符串。
基本的 strtok 用法
此示例演示了使用 strtok 进行基本的字符串标记化。
#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 可以处理多个定界符字符,如下所示。
#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 函数。
#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 调用之间更改定界符。
#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;
}
此代码首先按分号拆分字符串,然后按等号拆分每个生成的标记。可以通过使用不同的定界符来实现嵌套标记化。但是,这种方法可能会令人困惑并可能导致错误。对于复杂的解析,请考虑使用专用解析库或编写自定义解析器。始终清晰地记录此类嵌套标记化。
逐行标记化文件
此示例演示了如何逐行读取文件并标记化每一行。
#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 的最佳实践
- 避免修改源字符串:如果需要原始字符串,请进行复制。
- 考虑线程安全性:在多线程代码中使用
strtok_s或strtok_r。 - 处理空标记:连续的定界符会产生空标记。
- 记录定界符更改:在调用之间更改定界符时。
- 检查 NULL 返回:在使用标记之前,请务必验证标记。
来源
本教程探讨了 strtok 函数,从基本用法到高级注意事项。虽然它在字符串解析方面功能强大,但始终要小心使用它,以防止程序中的安全问题和未定义行为。
作者
列表 C 标准库。