标准C库中是否有类似于 startsWith(str_a, str_b)
的函数?
它应该接受指向两个以 nullbytes 结尾的字符串的指针,并告诉我第一个字符串是否完全出现在第二个字符串的开头。
示例:
"abc", "abcdef" -> true
"abcdef", "abc" -> false
"abd", "abdcef" -> true
"abc", "abc" -> true
标准C库中是否有类似于 startsWith(str_a, str_b)
的函数?
它应该接受指向两个以 nullbytes 结尾的字符串的指针,并告诉我第一个字符串是否完全出现在第二个字符串的开头。
示例:
"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
函数仅比较从指向s1
的数组到指向s2
的数组中的最多n
个字符(跟在空字符后面的字符不会被比较)。
strncmp
和 strlen
的算法并不被称为“叫做 strncmp”。 - Jim BalterstartsWith()
函数。 - Sridhar Sarnobat显然,这里没有标准的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的解决方案,它避免了对`str`进行`strlen`操作(他是正确的,如果您使用`strncmp`而不是`memcmp`,则这是不必要的)。只有在(非常)大的字符串或紧密的循环中重复使用时才会产生影响,但当它有影响时,就非常重要。memcmp
替换 strncmp
,它会更快。因为两个字符串都已知至少有 lenpre
个字节,所以没有 UB。strncmp
检查两个字符串的每个字节是否为 NUL,但是 strlen
调用已经保证了没有任何 NUL。(但是当 pre
或 str
长度大于实际的公共初始序列时,仍然会有你提到的性能损失。) - Jim Baltermemcmp
不会侵犯其他答案,因此我已经在答案中进行了更改。 - T.J. Crowderstrlen
用于目标字符串,加上每个前缀的strlen
(除非是常量)和memcmp
(如果前缀比目标字符串长,则甚至不需要)。 - Jim Balter我可能会选择使用strncmp()
,但只是为了好玩,这里有一个基本的实现:
_Bool starts_with(const char *restrict string, const char *restrict prefix)
{
while(*prefix)
{
if(*prefix++ != *string++)
return 0;
}
return 1;
}
strncmp
慢,因为glibc的编写者肯定是这样的 :-) - Ciro Santilli OurBigBook.com使用 strstr()
函数。Stra == strstr(stra, strb)
The strstr() function finds the first occurrence of string2 in string1. The function ignores the null character (\0) that ends string2 in the matching process.
https://www.ibm.com/docs/en/i/7.4?topic=functions-strstr-locate-substring
我不是写优雅代码的专家,但...
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;
}
str
以prefix
开头,则返回true
,否则返回false
。/**
* strstarts - does @str start with @prefix?
* @str: string to examine
* @prefix: prefix to look for.
*/
bool strstarts(const char *str, const char *prefix)
{
return strncmp(str, prefix, strlen(prefix)) == 0;
}
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。 - thejh或者两种方法的结合:
_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还是长度(块大小)。
另一个想法是逐块进行比较。如果块不相等,则将该块与原始函数进行比较:
_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
的增加必须在指针增加之后。现已修复。 - shpc我使用这个宏:
#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)计算,因此没有运行时开销。
因为我运行了被接受的版本,但是遇到了一个非常长的字符串问题,所以我不得不添加以下逻辑:
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;
}