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
对此没有标准功能,但您可以定义
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
显然这没有标准的 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
)。仅对(非常)大的字符串或在紧密循环中重复使用很重要,但是当它很重要时,它很重要。
我可能会选择strncmp()
,但只是为了好玩,一个原始的实现:
_Bool starts_with(const char *restrict string, const char *restrict prefix)
{
while(*prefix)
{
if(*prefix++ != *string++)
return 0;
}
return 1;
}
使用strstr()
功能。 Stra == strstr(stra, strb)
我不是编写优雅代码的专家,但是...
int prefix(const char *pre, const char *str)
{
char cp;
char cs;
if (!*pre)
return 1;
while ((cp = *pre++) && (cs = *str++))
{
if (cp != cs)
return 0;
}
if (!cs)
return 0;
return 1;
}
优化(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;
}
因为我运行了接受的版本并且遇到了一个很长的 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));
}
}
编辑:下面的代码不起作用,因为如果 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
只是猜测。必须为使用的输入数据和硬件选择它。
我使用这个宏:
#define STARTS_WITH(string_to_check, prefix) (strncmp(string_to_check, prefix, ((sizeof(prefix) / sizeof(prefix[0])) - 1)) ? 0:((sizeof(prefix) / sizeof(prefix[0])) - 1))
如果字符串以前缀开头,则返回前缀长度。这个长度是评估编译时间(sizeof),所以没有运行时开销。