2011-01-22 15 views
57

C'è qualcosa come startsWith(str_a, str_b) nella libreria C standard?Come verificare se una stringa inizia con un'altra stringa in C?

Dovrebbe portare i puntatori a due stringhe che terminano con nullbyte e dirmi se anche il primo appare completamente all'inizio del secondo.

Esempi:

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

Penso che il tuo terzo esempio dovrebbe avere un risultato vero. –

+0

@Burr: Sì, giusto. – thejh

risposta

52

Apparentemente non c'è alcuna funzione standard C per questo. Quindi:

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

Si noti che quanto sopra è bella e chiara, ma se si sta facendo in un loop stretto o di lavoro con molto stringhe di grandi dimensioni, non può offrire le migliori prestazioni, come esegue la scansione dell'intera lunghezza di entrambe le stringhe in avanti (strlen). Soluzioni come wj32's o Christoph's possono offrire prestazioni migliori (anche se this comment sulla vettorizzazione è al di là della mia conoscenza di C). Si noti anche Fred Foo's solution che evita strlen su str (ha ragione, non è necessario). Importa solo stringhe (molto) grandi o uso ripetuto in cicli stretti, ma quando è importante, conta.

+5

Devo dire che la * solita * cosa sarebbe che la stringa fosse il primo parametro e il prefisso fosse il secondo. Ma li ho mantenuti come sopra perché sembrava che la tua domanda fosse incorniciata ... L'ordine dipende interamente da te, ma in realtà avrei dovuto farlo in senso contrario - la maggior parte delle funzioni di stringa prende la stringa completa come primo argomento, la sottostringa come seconda. –

+1

Questa è una soluzione elegante, ma presenta alcuni problemi di prestazioni. Un'implementazione ottimizzata non guarderebbe mai più di min (strlen (pre), strlen (str)) caratteri da ogni stringa, né guarderebbe mai oltre la prima mancata corrispondenza. Se le corde fossero lunghe, ma le prime divergenze erano comuni, sarebbe molto leggero. Ma dal momento che questa implementazione prende tutta la lunghezza di entrambe le stringhe in primo piano, forza le prestazioni nel caso peggiore, anche se le stringhe differiscono nel primo carattere. Se questo è davvero importante dipende dalle circostanze, ma è un potenziale problema. –

+0

@TomKarzes: Assolutamente, sono stato rovinato da lingue/ambienti in cui la lunghezza della stringa è un valore noto piuttosto che uno che dobbiamo capire. :-) [soluzione di wj32] (https://stackoverflow.com/a/4771055/157247) offre prestazioni molto migliori. Importa solo per stringhe (molto) grandi o anelli stretti, ma quando è importante, conta. –

101

Non c'è alcuna funzione standard per questo, ma è possibile definire

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

Non abbiamo preoccupare di str essere più breve di pre perché secondo lo standard C (7.21.4.4/2):

La funzione strncmp confronta non più di n caratteri (caratteri che seguono un carattere nullo non vengono confrontati) dalla matrice puntato dal s1 all'array puntato da s2."

+4

Perché la risposta è no? Chiaramente, la risposta è sì, si chiama 'strncmp'. – Jasper

5

Non sono un esperto in scrittura di codice elegante, ma ...

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

Usa strstr() funzione. Stra == strstr(stra, strb)

+1

che sembra essere un modo alquanto arretrato di farlo - si passerà attraverso tutto il resto anche se dovrebbe essere chiaro dal segmento iniziale molto breve se strb è un prefisso o meno. – StasM

22

probabilmente sarei andare con strncmp(), ma solo per divertimento un'implementazione grezzo:

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

    return 1; 
} 
+6

Mi piace di più: non c'è motivo di scansionare una delle stringhe per una lunghezza. –

+1

Probabilmente andrei anche con strlen + strncmp, ma anche se in effetti funziona, tutte le polemiche sulla sua vaga definizione mi stanno scoraggiando. Quindi userò questo, grazie. –

+4

È probabile che sia più lento di 'strncmp', a meno che il compilatore non sia veramente valido per la vettorizzazione, perché gli scrittori di glibc sono sicuri :-) –

1

Perché ho eseguito la versione accettata e ha avuto un problema con una lunghissima str, ho dovuto aggiungere nel logica seguente:

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

ottimizzato (v.2 - corretta.):

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") 'restituisce 1, 'startsWith (" \ 1 "," \ 1 ")' restituisce anche 1 – thejh

+0

Hai ragione. C'era una linea sbagliata. – Zloten

+0

Sembra buono ora! :) – thejh

-5

Ottimizzato:

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

    return *s2 == 0; 
} 
+2

Hai provato 'StartsWith (" "," ")'? – Neil

Problemi correlati