考虑以下两种ignore_delims
函数的实现:
char* ignore_delims_v1(char* input, char* delims)
{
while (in(*input, delims))
++input;
return input;
}
char* ignore_delims_v2(char* input, char* delims)
{
ssize_t len = strlen(delims);
do {
for (int i = 0; i < len; ++i)
input = skip_char(input, delims[i]);
} while(in(*input, delims));
return input;
}
其中in(char c, char* test)
是一个函数,如果c
在test
中,则返回1
(通过将test
与c
逐个检查),而skip_char(char* str, char c)
返回第一个出现!= c
。
如果以下假设通常为真,则第二个版本在统计上应该更快:
- 大多数标记在被多个字符分隔时,通常不会混合多个不同的字符。
例如:
field1 field2\t\tfield3 field4\n
field1 field2\tfield3\t\t field4\n
比这个更常见:
field1 \t field2\t\t field3 field4 \n
\tfield1 field2\tfield3 \tfield4\n
在第一个例子中,每对字段都是由``,\t
或\n
分隔的,但是没有一个分隔字符串包含超过一个不同的字符。然而,第二个例子中的“分隔字符串”包含多个不同的字符。
我认为大多数情况下,第一个例子比第二个更真实,至少当你解析非自然语言的文本时,比如表格、命令输出等,这些都是一直在解析的东西。
如果我的假设是正确的,那么我认为第二个实现模式在统计上一定比第一个快得多,因为它试图一次消费同一个序列的连续序列,这是比第一个更常见的情况。我认为执行字符检查的数量会大大减少。
现在最后一个问题,当strtok
必须找到下一个标记并且必须首先跳过分隔符时,strtok
使用的实现是更类似于我的第一个版本,还是更类似于第二个版本?
2条答案
按热度按时间qlvxas9a1#
strtok
是实现的一部分,因此对此没有单一的答案。您可以检查示例glibc,它不包括您的优化。如果你搜索字符串搜索算法的文献,你会发现有很多合理的方法。
strtok
是标准库中的一个简单的、没有针对数据进行优化的函数,它肯定已经过时了。一个明显的迹象是它不是可重入的。使用strtok_r
代替。yacmzcpb2#
你提出的实现依赖于函数
in
和skip_char
,你没有发布。如果in
有一个循环来测试字符与所有接受的字符,在某些情况下,转置循环以有效地跳过相同分隔符的序列可能会有一些好处。但是请注意,您的方法是低效的,因为它会跳过单词而不是分隔符序列。此外,如果分隔符字符串只有一个字符,那么它将使用更多的测试。
大多数
strtok
的实现依赖于strspn()
、strcspn()
、strpbrk
或自定义代码。可能会利用一些常见的情况:in
功能等效的功能:if (a[c >> 5] & (1U << (c & 31)))
最后,
strtok
可能不值得优化:甚至不应该再使用它,因为它的语义令人困惑。这个函数使用了一个静态的内部状态,这使得它不可重入并且可能是非线程安全的。使用strtok_r
,或strspn
、strcspn
、strpbrk
的组合。