C语言 strtok是如何真正实现的?(业绩问题)

eyh26e7m  于 2023-10-16  发布在  其他
关注(0)|答案(2)|浏览(144)

考虑以下两种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)是一个函数,如果ctest中,则返回1(通过将testc逐个检查),而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使用的实现是更类似于我的第一个版本,还是更类似于第二个版本?

qlvxas9a

qlvxas9a1#

strtok是实现的一部分,因此对此没有单一的答案。您可以检查示例glibc,它不包括您的优化。
如果你搜索字符串搜索算法的文献,你会发现有很多合理的方法。strtok是标准库中的一个简单的、没有针对数据进行优化的函数,它肯定已经过时了。一个明显的迹象是它不是可重入的。使用strtok_r代替。

yacmzcpb

yacmzcpb2#

你提出的实现依赖于函数inskip_char,你没有发布。如果in有一个循环来测试字符与所有接受的字符,在某些情况下,转置循环以有效地跳过相同分隔符的序列可能会有一些好处。
但是请注意,您的方法是低效的,因为它会跳过单词而不是分隔符序列。此外,如果分隔符字符串只有一个字符,那么它将使用更多的测试。
大多数strtok的实现依赖于strspn()strcspn()strpbrk或自定义代码。可能会利用一些常见的情况:

  • 它们可以测试分隔符字符串是否具有单个字符,并使用简单的循环跳过分隔符或跳到下一个分隔符。
  • 对于较长的分隔符集,它们可以使用位数组在单个测试中执行与in功能等效的功能:if (a[c >> 5] & (1U << (c & 31)))
  • 它们可以使用精心设计的宏在编译时测试单个字符串。

最后,strtok可能不值得优化:甚至不应该再使用它,因为它的语义令人困惑。这个函数使用了一个静态的内部状态,这使得它不可重入并且可能是非线程安全的。使用strtok_r,或strspnstrcspnstrpbrk的组合。

相关问题