2011-01-22 7 views
57

¿Hay algo como startsWith(str_a, str_b) en la biblioteca C estándar?¿Cómo comprobar si una cadena comienza con otra cadena en C?

Debe tomar punteros a dos cadenas que terminan con nullbytes, y dígame si la primera también aparece completamente al comienzo de la segunda.

Ejemplos:

"abc", "abcdef" -> true 
"abcdef", "abc" -> false 
"abd", "abdcef" -> true 
"abc", "abc" -> true 
+2

Creo que su tercer ejemplo debería tener un resultado verdadero. –

+0

@Burr: Sí, claro. – thejh

Respuesta

52

Al parecer no hay ninguna función de C estándar para esto. Por lo tanto:

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

Tenga en cuenta que lo anterior es agradable y claro, pero si lo haces en un bucle estrecho o trabajar con muy grandes cadenas, que no puede ofrecer el mejor rendimiento, como escanea la longitud total de ambas cadenas por adelantado (strlen). Las soluciones como wj32's o Christoph's pueden ofrecer un mejor rendimiento (aunque this comment sobre la vectorización está más allá de mi conocimiento de C). También tenga en cuenta Fred Foo's solution que evita strlen en str (tiene razón, no es necesario). Solo importa para cuerdas (muy) grandes o uso repetido en bucles apretados, pero cuando importa, importa.

+5

Debo mencionar que la cosa * usual * sería que la cadena sea el primer parámetro, y el prefijo sea el segundo. Pero los mantuve como los de arriba porque parecían ser la forma en que se formuló su pregunta ... El orden es totalmente de usted, pero realmente debería haberlo hecho al revés: la mayoría de las funciones de cuerdas toman la secuencia completa como el primer argumento, la subcadena como el segundo. –

+1

Esta es una solución elegante, pero tiene algunos problemas de rendimiento. Una implementación optimizada nunca miraría más que los caracteres mínimos (strlen (pre), strlen (str)) de cada cadena, ni miraría más allá de la primera discrepancia. Si las cuerdas fueran largas, pero los desajustes tempranos eran comunes, sería muy ligero. Pero dado que esta implementación toma la longitud total de ambas cadenas desde el principio, fuerza el peor de los casos, incluso si las cadenas difieren en el primer carácter. Si esto realmente importa depende de las circunstancias, pero es un problema potencial. –

+0

@TomKarzes: Absolutamente, me he echado a perder por idiomas/entornos donde la longitud de la cadena es un valor conocido en lugar de uno que tenemos que averiguar. :-) [la solución de wj32] (https://stackoverflow.com/a/4771055/157247) ofrece un rendimiento mucho mejor. Solo importa para cuerdas (muy) grandes o bucles apretados, pero cuando importa, es importante. –

101

No hay función estándar para esto, pero se puede definir

bool prefix(const char *pre, const char *str) 
{ 
    return strncmp(pre, str, strlen(pre)) == 0; 
} 

No tenemos que preocuparnos de str más corta que pre porque de acuerdo con el estándar C (7.21.4.4/2):

Función strncmp compara no más de n caracteres (caracteres que siguen un carácter nulo no se comparan) desde el array apuntado por s1 a la matriz apuntada por s2."

+4

¿Por qué la respuesta es no? Claramente, la respuesta es sí, se llama 'strncmp'. – Jasper

5

No soy un experto en la escritura de código elegante, pero ...

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

Uso strstr() función. Stra == strstr(stra, strb)

+1

que parece ser una forma un tanto atrasada de hacerlo: pasarás por todo el proceso, aunque debería estar claro desde un segmento inicial muy corto si strb es un prefijo o no. – StasM

22

probablemente iría con strncmp(), pero sólo por diversión una implementación prima:

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

    return 1; 
} 
+6

Me gusta esto mejor - no hay ninguna razón para escanear cualquiera de las cadenas de una longitud. –

+1

Probablemente vaya con strlen + strncmp también, pero aunque de hecho funciona, toda la controversia sobre su vaga definición me está desanimando. Entonces usaré esto, gracias. –

+4

Es probable que sea más lento que 'strncmp', a menos que el compilador sea realmente bueno en la vectorización, porque los escritores glibc están seguros :-) –

1

Porque corrió la versión aceptada y tenía un problema con un muy largo str, he tenido que añadir en el siguiente lógica:

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

optimizado (v.2 - corregido.):

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

voto negativo:' startsWith ("\ 2", "\ 1") 'devuelve 1, 'startsWith (" \ 1 "," \ 1 ")' también devuelve 1 – thejh

+0

Tiene razón. Había una línea incorrecta. – Zloten

+0

¡Se ve bien ahora! :) – thejh

-5

Optimizado:

boolean StartsWith(char *s1, char *s2) 
{ 
    while (*s1++ == *s2++) 
    { 
    } 

    return *s2 == 0; 
} 
+2

¿Intentó 'StartsWith (" "," ")'? – Neil

Cuestiones relacionadas