2011-01-22 21 views
57

Czy jest coś takiego, jak startsWith(str_a, str_b) w standardowej bibliotece C?Jak sprawdzić, czy ciąg zaczyna się od innego ciągu w C?

Powinien wziąć wskaźniki do dwóch ciągów kończących się na nullbytes i powiedzieć mi, czy pierwszy pojawia się również na początku drugiego.

Przykłady:

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

Myślę, że twój trzeci przykład powinien mieć prawdziwy wynik. –

+0

@Burr: Tak, racja. – thejh

Odpowiedz

52

Widocznie nie ma standardowych funkcji C do tego. Więc:

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

Należy zauważyć, że powyższe jest ładne i jasne, ale jeśli robisz to w ciasnej pętli lub pracy z samych dużych ciągów, nie mogą zaoferować najlepszą wydajność, jak skanuje pełną długość obu ciągów z przodu (strlen). Rozwiązania takie jak wj32's lub Christoph's mogą oferować lepszą wydajność (chociaż this comment o wektoryzacji jest poza moją wartością C). Zanotuj także Fred Foo's solution, co pozwala uniknąć strlen na str (ma rację, to niepotrzebne). Ma znaczenie tylko dla (bardzo) dużych ciągów lub wielokrotnego użycia w ciasnych pętlach, ale gdy jest to ważne, to ma znaczenie.

+5

Powinienem wspomnieć, że * zwykłe * było by string był pierwszym parametrem, a prefiks byłby drugi. Ale zachowałem je tak jak powyżej, ponieważ wyglądało na to, że twoje pytanie zostało sformułowane ... Kolejność zależy wyłącznie od ciebie, ale naprawdę powinienem był zrobić to w inny sposób "okrągło" - większość funkcji ciągu bierze cały łańcuch jako pierwszy argument, podciąg jako drugi. –

+1

To jest eleganckie rozwiązanie, ale ma pewne problemy z wydajnością. Zoptymalizowana implementacja nigdy nie spojrzy na więcej niż min (strlen (pre), strlen (str)) znaków z każdego ciągu, ani też nigdy nie wyjdzie poza pierwszą niedopasowanie. Jeśli struny były długie, ale wczesne niedopasowania były częste, to byłyby bardzo lekkie. Ale ponieważ implementacja ta zajmuje całą długość obu łańcuchów bezpośrednio z przodu, wymusza to najgorszą możliwą wydajność, nawet jeśli łańcuchy różnią się od pierwszego znaku. To, czy to naprawdę ma znaczenie, zależy od okoliczności, ale jest potencjalnym problemem. –

+0

@TomKarzes: Absolutnie, zostałem rozpieszczony przez języki/środowiska, w których długość łańcucha jest wartością znaną, a nie jedną, którą musimy wymyślić. :-) [rozwiązanie wj32] (https://stackoverflow.com/a/4771055/157247) oferuje znacznie lepszą wydajność. Ma znaczenie tylko dla (bardzo) dużych ciągów lub ciasnych pętli, ale kiedy ma to znaczenie, ma to znaczenie. –

101

Nie ma standardowych funkcji dla tego, ale można zdefiniować

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

Nie musimy się martwić o str być krótszy niż pre ponieważ zgodnie ze standardem C (7.21.4.4/2):

Funkcja strncmp porównuje nie więcej niż n znaków (znaki, które następują znak null nie są porównywane) od tablicy wskazywanej przez s1 do tablicy wskazywanej przez s2.”

+4

Dlaczego odpowiedź brzmi nie? Oczywiście odpowiedź brzmi "tak", nazywa się "strncmp". – Jasper

5

nie jestem ekspertem w pisaniu kodu elegancki, ale ...

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

Zastosowanie strstr() funkcja. Stra == strstr(stra, strb)

+1

, który wydaje się być nieco wstecznym sposobem na zrobienie tego - przejdziesz przez całą stra, chociaż powinno być jasne z bardzo krótkiego początkowego segmentu, jeśli strb jest prefiksem lub nie. – StasM

22

I pewnie iść z strncmp(), ale tylko dla zabawy surowy realizacja:

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

    return 1; 
} 
+6

Podoba mi się to najlepiej - nie ma powodu, aby skanować dowolny z ciągów na długość. –

+1

Prawdopodobnie pójdę też ze strlenem + strncmp, ale chociaż tak naprawdę działa, wszystkie kontrowersje wokół niejasnej definicji mnie zniechęcają. Więc wykorzystam to, dzięki. –

+4

To prawdopodobnie będzie wolniejsze niż 'strncmp', chyba że twój kompilator jest naprawdę dobry w wektoryzacji, ponieważ pisarze glibc z pewnością są :-) –

1

Ponieważ wpadłem wersję przyjętą i miał problem z bardzo długim str, musiałem włożyć w po logika:

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

Optimized (v.2 - poprawione.):

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

głosowanie negatywne:' startsWith ("\ 2", "\ 1") 'zwraca 1, 'startsWith (" \ 1 "," \ 1 ")' również zwraca 1 – thejh

+0

Masz rację. Była jedna zła linia. – Zloten

+0

Wygląda dobrze teraz! :) – thejh

-5

Zoptymalizowany:

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

    return *s2 == 0; 
} 
+2

Czy wypróbowałeś 'StartsWith (" "," ")'? – Neil

Powiązane problemy