startsWith(str_a, str_b)
标准C库中是否有类似内容?
它应该使用指向以空字节结尾的两个字符串的指针,并告诉我第一个字符串是否也完全出现在第二个字符串的开头。
例子:
"abc", "abcdef" -> true
"abcdef", "abc" -> false
"abd", "abdcef" -> true
"abc", "abc" -> true
startsWith(str_a, str_b)
标准C库中是否有类似内容?
它应该使用指向以空字节结尾的两个字符串的指针,并告诉我第一个字符串是否也完全出现在第二个字符串的开头。
例子:
"abc", "abcdef" -> true
"abcdef", "abc" -> false
"abd", "abdcef" -> true
"abc", "abc" -> true
Answers:
显然,对此没有标准的C函数。所以:
bool startsWith(const char *pre, const char *str)
{
size_t lenpre = strlen(pre),
lenstr = strlen(str);
return lenstr < lenpre ? false : memcmp(pre, str, lenpre) == 0;
}
请注意,上面的内容很好理解,但是如果您是在紧密循环中使用或处理非常大的字符串,则它不能提供最佳性能,因为它会在前面扫描两个字符串的全长(strlen
)。诸如wj32或Christoph的解决方案可能会提供更好的性能(尽管有关向量化的评论超出了我对C的理解)。另请注意,Fred Foo避免使用strlen
on的解决方案str
(他是对的,如果您使用strncmp
代替,则没有必要memcmp
)。仅对于(非常)大的字符串或紧密循环中的重复使用很重要,但是当它很重要时,它很重要。
memcmp
在strncmp
这里代替它,它的速度更快。没有UB,因为两个字符串都至少具有lenpre
字节。strncmp
检查两个字符串的每个字节是否为NUL,但strlen
调用已保证没有任何调用。(但是,当它pre
或str
它比实际的通用初始序列长时,它仍然具有您提到的性能损失。)
memcmp
此处使用的不是上面的其他答案,因此我继续在答案中进行了更改。
strlen
并memcmp
可以非常快的硬件指令来实现,而strlen
S可以把串到缓存中,避免了双存储命中。在这样的机器上,strncmp
可以像这样实现为2strlen
和a memcmp
,但是对于库编写者来说这样做是有风险的,因为对于带有短公共前缀的长字符串来说,这样做可能会花费更长的时间。这里的命中是明确的,并且strlen
s每次仅执行一次(Fred Foo的strlen
+strncmp
将执行3)。
没有标准功能,但是您可以定义
bool prefix(const char *pre, const char *str)
{
return strncmp(pre, str, strlen(pre)) == 0;
}
我们不必担心str
会比pre
因为C标准(7.21.4.4/2)而短:
该
strncmp
函数n
从所指向的数组到所s1
指向的数组的字符比较不超过字符(不比较跟随空字符的字符)s2
。
strncmp
。
strncmp
而strlen
不是“叫STRNCMP”。
我可能会选择strncmp()
,但只是为了好玩一个原始实现:
_Bool starts_with(const char *restrict string, const char *restrict prefix)
{
while(*prefix)
{
if(*prefix++ != *string++)
return 0;
}
return 1;
}
strncmp
除非您的编译器真正擅长矢量化,否则这可能会慢一些,因为glibc作家肯定是:-)
使用strstr()
功能。 Stra == strstr(stra, strb)
优化(v.2。-已更正):
uint32 startsWith( const void* prefix_, const void* str_ ) {
uint8 _cp, _cs;
const uint8* _pr = (uint8*) prefix_;
const uint8* _str = (uint8*) str_;
while ( ( _cs = *_str++ ) & ( _cp = *_pr++ ) ) {
if ( _cp != _cs ) return 0;
}
return !_cp;
}
startsWith("\2", "\1")
返回1,startsWith("\1", "\1")
也返回1
因为我运行了可接受的版本,并且str的长度很长,所以我不得不添加以下逻辑:
bool longEnough(const char *str, int min_length) {
int length = 0;
while (str[length] && length < min_length)
length++;
if (length == min_length)
return true;
return false;
}
bool startsWith(const char *pre, const char *str) {
size_t lenpre = strlen(pre);
return longEnough(str, lenpre) ? strncmp(str, pre, lenpre) == 0 : false;
}
或两种方法的组合:
_Bool starts_with(const char *restrict string, const char *restrict prefix)
{
char * const restrict prefix_end = prefix + 13;
while (1)
{
if ( 0 == *prefix )
return 1;
if ( *prefix++ != *string++)
return 0;
if ( prefix_end <= prefix )
return 0 == strncmp(prefix, string, strlen(prefix));
}
}
编辑:下面的代码做NOT工作,因为如果STRNCMP返回0,如果终止0或长度(BLOCK_SIZE)达到它是未知的。
另一个想法是按块比较。如果该块不相等,则将该块与原始函数进行比较:
_Bool starts_with_big(const char *restrict string, const char *restrict prefix)
{
size_t block_size = 64;
while (1)
{
if ( 0 != strncmp( string, prefix, block_size ) )
return starts_with( string, prefix);
string += block_size;
prefix += block_size;
if ( block_size < 4096 )
block_size *= 2;
}
}
常量13
,64
,4096
,还有的幂block_size
都只是猜测。必须为使用的输入数据和硬件选择它。
block_size
增量必须在指针增量之后。现在已修复。