Existe-t-il quelque chose comme startsWith(str_a, str_b)
dans la bibliothèque standard C?
Cela devrait prendre des pointeurs sur deux chaînes qui se terminent par nullbytes et me dire si la première apparaît également complètement au début de la seconde.
Exemples:
"abc", "abcdef" -> true
"abcdef", "abc" -> false
"abd", "abdcef" -> true
"abc", "abc" -> true
Apparemment, il n’ya pas de fonction C standard pour cela. Alors:
bool startsWith(const char *pre, const char *str)
{
size_t lenpre = strlen(pre),
lenstr = strlen(str);
return lenstr < lenpre ? false : strncmp(pre, str, lenpre) == 0;
}
Notez que ce qui précède est beau et clair, mais si vous le faites dans une boucle serrée ou si vous travaillez avec très grosses chaînes, il se peut que la performance ne soit pas optimale car il scanne toute la longueur des deux chaînes à l'avant (strlen
). Des solutions comme wj32 ou Christoph peuvent offrir de meilleures performances (bien que ce commentaire à propos de la vectorisation est au-dessus de mes connaissances de C). Notez aussi La solution de Fred Foo qui évite strlen
sur str
(il a raison, c'est inutile). Ne concerne que les chaînes (très) volumineuses ou les utilisations répétées dans des boucles serrées, mais lorsque c'est important, c'est important.
Il n'y a pas de fonction standard pour cela, mais vous pouvez définir
bool prefix(const char *pre, const char *str)
{
return strncmp(pre, str, strlen(pre)) == 0;
}
Nous n'avons pas à nous soucier de str
étant plus court que pre
car, selon la norme C (7.21.4.4/2):
La fonction
strncmp
ne compare pas plus den
caractères (les caractères qui suivent un caractère nul ne sont pas comparés) du tableau pointé pars1
au tableau pointé pars2
. "
J'irais probablement avec strncmp()
, mais juste pour le plaisir, une implémentation brute:
_Bool starts_with(const char *restrict string, const char *restrict prefix)
{
while(*prefix)
{
if(*prefix++ != *string++)
return 0;
}
return 1;
}
Je ne suis pas un expert en écriture de code élégant, mais ...
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;
}
Utilisez la fonction strstr()
. Stra == strstr(stra, strb)
Optimisé (v.2. - corrigé):
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;
}
Parce que je courais la version acceptée et que je rencontrais un problème avec une très longue chaîne, j'ai dû ajouter la logique suivante:
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;
}
Ou une combinaison des deux approches:
_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));
}
}
Une autre idée est de comparer les blocs. Si le bloc n'est pas égal, comparez ce bloc avec la fonction d'origine:
_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);
if ( block_size < 4096 )
block_size *= 2;
string += block_size;
prefix += block_size;
}
}
Les constantes 13
, 64
, 4096
ainsi que l’exponentiation de block_size
ne sont que des suppositions. Il devrait être sélectionné pour les données d'entrée et le matériel utilisés.