C 中如何检查一个字符串是否以另一个字符串开头?

问题描述 投票:0回答:10

标准 C 库中有类似

startsWith(str_a, str_b)
的东西吗?

它应该获取指向两个以空字节结尾的字符串的指针,并告诉我第一个字符串是否也完全出现在第二个字符串的开头。

示例:

"abc", "abcdef" -> true
"abcdef", "abc" -> false
"abd", "abdcef" -> true
"abc", "abc"    -> true
c string comparison startswith
10个回答
224
投票

没有标准函数,但您可以定义

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
指向的数组进行比较。"


93
投票

显然没有标准的 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;
}

请注意,上面的内容很好很清晰,但是如果您在紧密循环中执行此操作或使用very大字符串,则它不会提供最佳性能,因为它会预先扫描两个字符串的完整长度(

strlen
)。像 wj32'sChristoph's 这样的解决方案可能会提供更好的性能(尽管关于矢量化的 this comment 超出了我的 C 知识范围)。另请注意 Fred Foo 的解决方案,它避免了
strlen
上的
str
(他是对的,如果您使用
strncmp
而不是
memcmp
,则没有必要)。仅对(非常)大的字符串或在紧密循环中重复使用很重要,但当它很重要时,它很重要。


40
投票

我可能会选择

strncmp()
,但只是为了好玩,原始实现:

int starts_with(const char *restrict string, const char *restrict prefix)
{
    while(*prefix)
    {
        if(*prefix++ != *string++)
            return 0;
    }

    return 1;
}

给予:


5
投票

使用

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


4
投票

我不是编写优雅代码的专家,但是......

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;
}

3
投票

我注意到Linux Kernel中的以下函数定义。如果

true
str
开头,则返回
prefix
,否则返回
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;
}

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;
}

0
投票

因为我运行了接受的版本并且遇到了很长的 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;
}

0
投票

或者两种方法的组合:

_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
的求幂都只是猜测。必须根据所使用的输入数据和硬件来选择它。


0
投票

我使用这个宏:

#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),因此没有运行时开销。

© www.soinside.com 2019 - 2024. All rights reserved.