C语言 - 确定使用的分隔符 - strtok()

10

假设我正在像这样使用strtok()..

char *token = strtok(input, ";-/");

有没有办法找出实际使用的令牌?例如,如果输入如下:

Hello there; How are you? / I'm good - End

我能否找出用于每个令牌的定界符?我需要能够根据跟随令牌的定界符输出特定消息。


1
关于信息,当您使用strtok时:此接口已被strsep(3)所取代。 - Geoffroy
不过,如果您提出一个要求,寻找一个能够实现这个功能的版本,可能会有人编写一个。char *strtok_new(char *string, const char *delimiters,char *matched); - Scooter
1
请注意,strsep 不属于 C 或 Posix(尽管 strtok_r 属于 Posix,strtok_s 属于 C11),但它属于 LSB 和 BSD。因此,在某些系统上,strtok 已被 strsep 废弃,而在其他系统上则没有。关于 man 手册的事情是,它们只适用于运行 man 的系统。 - Steve Jessop
3个回答

9

重要提示: strtok 不是可重入的,应该使用 strtok_r 来代替。

你可以通过保存原始字符串的副本,并查找当前标记在该副本中的偏移来实现:

char str[] = "Hello there; How are you? / I'm good - End";
char *copy = strdup(str);
char *delim = ";-/";
char *res = strtok( str, delim );
while (res) {
    printf("%c\n", copy[res-str+strlen(res)]);
    res = strtok( NULL, delim );
}
free(copy);

这会打印

;
/
-

演示 #1

编辑:处理多个分隔符

如果您需要处理多个分隔符,则确定当前分隔符序列的长度会稍微复杂一些:现在您需要在决定分隔符序列的长度之前找到下一个标记。数学并不复杂,只要记住NULL需要特殊处理即可:

char str[] = "(20*(5+(7*2)))+((2+8)*(3+6*9))";
char *copy = strdup(str);
char *delim = "*+()";
char *res = strtok( str, delim );
while (res) {
    int from = res-str+strlen(res);
    res = strtok( NULL, delim );
    int to = res != NULL ? res-str : strlen(copy);
    printf("%.*s\n", to-from, copy+from);
}
free(copy);

Demo #2


谢谢,这正是我希望实现的行为。 - Andrew Backes
@dasblinkenlight,如果您有多个分隔符连续出现,则无法正常工作。例如,考虑算术表达式205+(72)的标记化,其中您将delim定义为“+-/*()”,并且您对运算符和操作数的标记化感兴趣。 +(将无法正确标记化。 - David
1
@David 你说得对,这段代码假设总是有一个分隔符。不过,有一个简单的解决方法 - 你只需要获取下一个标记来决定当前分隔符的运行长度(请参见编辑和演示)。 - Sergey Kalinichenko
@dasblinkenlight 谢谢。它可以工作,但仍然缺少前导分隔符。如果您想要评估表达式,则需要所有分隔符(包括前导分隔符)。此外,您还必须再次拆分分隔符,因为它会将多个分隔符一起打印。我发现使用 find_first_of() 或 find_first_not_of() 编写自定义标记生成器更加清晰。另外,boost tokens() 类可以很好地实现这一点。 - David
类似于https://dev59.com/-2HVa4cB1Zd3GeqPqtZn的C语言字符串分割。 - David

3
你无法做到。strtok会用空字符(为了终止当前返回的令牌)覆盖下一个分隔符字符,而不会存储它覆盖的先前值。第一次在你的示例字符串上调用strtok时,';'就永远消失了。
如果你保留原始字符串的未修改副本,则可以进行一些操作-给定相对于字符串起始位置的当前标记的空终止符索引,可以查看副本中相同索引处的内容。
当然,这可能比编写自己的代码来分隔字符串更糟糕。如果你可以接受结果令牌没有被空字符终止,那么可以使用strpbrk或strcspn。

2

man 3 strtok

strtok()和strtok_r()函数返回指向字符串中每个后续标记的开头的指针,在用NUL字符替换标记本身后。当没有更多标记时,返回一个空指针。

但是通过一些指针算术运算,你可以做到这样:

char* string = "Hello,World!";
char* dup = strdup(string);

char* world = strtok(string, ",");
char delim_used = dup[world - string];

free(dup);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接