2009-04-13 13 views
29

Chcę się upewnić, że mój ciąg kończy się na ".foo". Używam C, języka, którego nie znam. Najlepszym sposobem, jaki udało mi się to zrobić, jest poniżej. Każdy guru C chce się upewnić, że robię to elegancko i mądrze?Jak porównać końcówki ciągów w C?

int EndsWithFoo(char *str) 
{ 
    if(strlen(str) >= strlen(".foo")) 
    { 
     if(!strcmp(str + strlen(str) - strlen(".foo"), ".foo")) 
     { 
      return 1; 
     } 
    } 
    return 0; 
} 

Odpowiedz

2

S strlen(".foo") s nie są wymagane. Jeśli naprawdę chciałeś, aby był elastyczny, możesz użyć sizeof ".foo" - 1 - stałej czasu kompilacji.

Również sprawdzanie łańcucha zerowego byłoby dobre.

+0

Popraw mnie, jeśli się mylę, ale nie jest sizeof (".foo") 5, ale strlen (". Foo") 4? Myślałem, że strlen jest łatwiejszy do odczytania, ponieważ mam tu do czynienia z długością łańcucha. Kompilator powinien zoptymalizować go do stałej ... Jak wygląda reszta funkcji? –

+0

W szczególności nie jest to konieczne, ponieważ wiemy już, jak długo ".foo" jest. – Chuck

+0

Czy ".foo" nie jest znakiem stałym? Nawet jeśli liczy się jako tablica, ma pięć znaków, ponieważ w formie tablicy ma terminator "\ 0". –

7

Nie mam teraz dostępu do kompilatora, więc czy ktoś może mi powiedzieć, czy to działa?

#include <stdio.h> 
#include <string.h> 

int EndsWithFoo(const char* s); 

int 
main(void) 
{ 
    printf("%d\n", EndsWithFoo("whatever.foo")); 

    return 0; 
} 

int EndsWithFoo(const char* s) 
{ 
    int ret = 0; 

    if (s != NULL) 
    { 
    size_t size = strlen(s); 

    if (size >= 4 && 
     s[size-4] == '.' && 
     s[size-3] == 'f' && 
     s[size-2] == 'o' && 
     s[size-1] == 'o') 
    { 
     ret = 1; 
    } 
    } 

    return ret; 
} 

W każdym razie, należy zakwalifikować jako parametr const, mówi wszystkim (w tym kompilator), że nie zamierza zmodyfikować ciąg.

+0

+1 najbardziej zoptymalizowany. Wolę taką wersję, gdy "foo" się nie zmienia! – dirkgently

+3

Wskazówka: Jeśli masz połączenie z Internetem, masz kompilator C dostępny pod adresem codepad.org –

+1

Prawo Murphy'ego mówi, że ".foo" zmieni się iw najbardziej nieodpowiednim momencie. – plinth

43

Nie wywoływać strlen więcej niż raz na ciąg.

int EndsWith(const char *str, const char *suffix) 
{ 
    if (!str || !suffix) 
     return 0; 
    size_t lenstr = strlen(str); 
    size_t lensuffix = strlen(suffix); 
    if (lensuffix > lenstr) 
     return 0; 
    return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; 
} 

int EndsWithFoo(const char *str) { return EndsWith(str, ".foo"); } 

EDYCJA: dodano kontrolę NULL dla pedantycznego. W przypadku ultra pedantycznego debatujemy, czy powinien on zwracać wartość niezerową, jeśli zarówno str, jak i sufiks są zarówno NULL.

+0

W tym przypadku możesz uciec używając strcmp() zamiast strncmp() (lub nawet memcmp()), ponieważ dokładnie wiemy, ile znaków pozostało w obu ciągach w tym miejscu, chociaż różnica prędkości prawie nie będzie zauważalny. –

+1

jakikolwiek telefon do znika ze zgromadzenia, tak szybko jak tylko włączasz optymalizacje, więc to prawdopodobnie jest przypadek przedwczesnej optymalizacji (chociaż struny C są na tyle obrzydliwe, aby myśleć o takich problemach) – Joey

+1

@Johannes: Jak to możliwe, dla łańcuchów innych niż literały łańcuchowe znane w czasie kompilacji? Z pewnością możesz wstawić kod strlen, ale na pewnym poziomie wciąż musisz znaleźć długość łańcucha. W przypadku stałych literałów ciągowych kompilator wie, jak długo to trwa, ale ogólnie nie jest to prawdą. Myśli? –

-3

Sugeruję, że najlepszym sposobem jest odwrócenie łańcucha, a następnie porównanie pierwszych n znaków.

Istnieje wiele przykładów funkcji cofania ciągów (nawet Joel powołuje się na to jako standardowe pytanie do wywiadu), więc po prostu zaimplementuj jeden z nich, a następnie porównaj odwrócone łańcuchy.

EDYCJA w odpowiedzi na downvotes. OK tak, to podejście wymaga zastosowania dodatkowego procesora lub pamięci, ale osoba przesłuchująca nie wskazuje na takie ograniczenia i wyraźnie poprosił o eleganckie rozwiązanie. Odwracanie strun, a następnie porównywanie z przodu jest o wiele bardziej eleganckie niż zakłócanie znajdowania końca strun i pracy w tył. Dla następnego programisty jest o wiele łatwiejsze do zrozumienia i utrzymania.

+0

Tak, to by działało, ale jest to alokacja pamięci dla kopii lub podwójny wsteczny, aby cofnąć uszkodzenia. – plinth

+0

Co z tego? Nic nie wskazuje na to, że działa pod procesorem lub w warunkach ograniczonej pamięci, a cofanie struny w celu porównania z przodu jest o wiele łatwiejsze do utrzymania niż zakłócanie znajdowania pozycji końcowej n i łamanie logiki stamtąd. – Cruachan

+0

W każdym razie musisz znaleźć koniec łańcucha, żeby wiedzieć, jak długo to trwało, żebyś wiedział, jak to odwrócić! Nie ma znaczenia, jak sobie z tym poradzisz, problem dotyczy końca łańcucha, więc musisz go znaleźć w taki czy inny sposób. – dreamlax

0

Można również uogólnić tak:

int endsWith(const char* text, const char* extn) 
{ 
    int result = 1; 
    int len = strlen(text); 
    int exprLen = strlen(extn); 
    int index = len-exprLen; 
    int count = 0; 

    if(len > exprLen) 
    { 
     for(; count < exprLen; ++count) 
     { 
      if(text[index + count] != extn[count]) 
      { 
       result = 0; 
       break; 
      } 

     } 
    } 
    else 
    { 
     result = 0; 
    } 
    return result; 
} 
+0

Zapomniałem, że istnieje strncmp ... – Naveen

2

Testowany kod obejmuje test:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

int ends_with_foo(const char *str) 
{ 
    char *dot = strrchr(str, '.'); 

    if (NULL == dot) return 0; 
    return strcmp(dot, ".foo") == 0; 
} 

int main (int argc, const char * argv[]) 
{ 
    char *test[] = { "something", "anotherthing.foo" }; 
    int i; 

    for (i = 0; i < sizeof(test)/sizeof(char *); i++) { 
     printf("'%s' ends %sin '.foo'\n", 
       test[i], 
       ends_with_foo(test[i]) ? "" : "not "); 
    } 
    return 0; 
} 
+0

Snap! Chociaż nie przejmuję się porównaniem wyniku strcmp() - po prostu zwróć go bezpośrednio. –

+0

zakładasz, że nie ma innych "." w ciągu wejściowym. – Naveen

+0

To nie ma znaczenia. Strcmp() zwróci tylko 0, jeśli istnieją dwa identyczne ciągi (np. Długość musi być taka sama). Porównanie zakończy się wcześniej, jeśli łańcuchy różnią się długością. –

8
int EndsWithFoo(char *string) 
{ 
    string = strrchr(string, '.'); 

    if(string != NULL) 
    return(strcmp(string, ".foo")); 

    return(-1); 
} 

Wrócimy 0, jeśli kończy się ".foo".

+0

Nice. Krótkie i czytelne. – EvilTeach

+0

* skromny łuk * jesteś bardzo miły :-) –

+4

Zwracane wartości powinny być odwrócone, ponieważ zero jest fałszywe, a niezerowe jest prawdziwe, a nazwa funkcji wskazuje powrót Boolean. – dreamlax

0

Może ...

bool endswith (const char* str, const char* tail) 
{ 
    const char* foo = strrstr (str, tail); 
    if (foo) 
    { 
    const int strlength = strlen (str); 
    const int taillength = strlen (tail); 
    return foo == (str + strlength - taillength); 
    } 
    return false; 
} 

endswith (str, ".foo"); 

Nawiasem mówiąc, rozwiązanie w pierwotnym pytaniu wygląda dobrze, inne niż wielokrotne apele strlen.

+0

strrstr() jest niestandardowy i nie istnieje, przynajmniej na glibc 2.15 – lumpidu

0

Jeśli zawsze coś poza kropką, możemy sobie na jakiś wskaźnik arytmetyki:

int EndsWithFoo (char *str) 
{ 
    int iRetVal = 0; 
    char * pchDot = strrchr (str, '.'); 

    if (pchDot) 
    { 
     if (strcmp (pchDot+1, "foo") == 0) 
     { 
     iRetVal = 1; 
     } 
    } 
    return iRetVal; 
} 

Oczywiście byłoby prawdopodobnie chcesz dodać trochę strlen tam, aby sprawdzić tam jest coś poza kropką: -)

NB - Nie uruchomiłem tego, aby to sprawdzić, ale wygląda na to, że jestem w porządku.

3

Jeśli można zmienić podpis swojej funkcji, a następnie spróbuj zmienić go

int EndsWith(char const * str, char const * suffix, int lenstr, int lensuf); 

Spowoduje to bezpieczniejsza, bardziej wielokrotnego użytku i bardziej wydajnego kodu:

  1. Dodawane const kwalifikatory upewni się, że nie zmienisz niepoprawnie ciągów wejściowych. Ta funkcja jest predykatem, więc zakładam, że nigdy nie ma ona skutków ubocznych.
  2. Sufiks do porównania jest przekazywany jako parametr, więc można zapisać tę funkcję, aby później ponownie użyć z innymi sufiksami.
  3. Podpis ten da ci możliwość przekazania długości napisów, jeśli już je znasz. Nazywamy to dynamic programming.

możemy zdefiniować funkcję tak:

int EndsWith(char const * str, char const * suffix, int lenstr, int lensuf) 
{ 
    if(! str && ! suffix) return 1; 
    if(! str || ! suffix) return 0; 
    if(lenstr < 0) lenstr = strlen(str); 
    if(lensuf < 0) lensuf = strlen(suffix); 
    return strcmp(str + lenstr - lensuf, suffix) == 0; 
} 

Oczywistym kontrargument dla dodatkowych parametrów jest to sugerować, że więcej hałasu w kodzie, lub kod mniej wyraziste.

-1

zrobiłbym to tak:

/** 
    * Return 0 if the string haystack ends with the string needle 
    * 
    * @param haystack the string to be analyzed 
    * @param needle the suffix string 
    * @return 0 if the string haystack ends with the string needle, 1 if not 
*/ 
int strbcmp(const char *haystack, const char *needle) { 
    int length; 
    if (haystack && needle && strlen(haystack) >= (length = strlen(needle)) && strlen(strstr(haystack, needle)) == length) return 0; 
    return 1; 
} 

Program badań jest:

#include <stdio.h> 
#include <string.h> 

int strbcmp(const char *haystack, const char *needle) { 
    int length; 
    if (haystack && needle && strlen(haystack) >= (length = strlen(needle)) && strlen(strstr(haystack,needle)) == length) return 0; 
    return 1; 
} 

int main (int argc, char * argv[]){ 
    char *a = "file1.gz"; 
    char *b = "1.gz"; 
    char *c = NULL; 
    char *d = "1.gzabc"; 

    printf("%s %s = %d\n",a,b,strbcmp(a,b)); 
    printf("%s %s = %d\n",a,c,strbcmp(a,c)); 
    printf("%s %s = %d\n",d,b,strbcmp(d,b)); 

    return 0; 
} 
+0

Nie kompiluje się nawet – lumpidu

+0

Przepraszamy za błędy w pisowni kodu źródłowego. Teraz jest dobrze. –

+1

Poniższy test nie działa z Twoją implementacją: char * e = "1.gzabc1.gz"; printf ("% s% s =% d \ n", e, b, strbcmp (e, b)); – lumpidu

0

Chciałbym używać mojego wersję:

bool endsWith(const char *filename, const char *ext) { 
    const uint len = strlen(filename); 
    const uint extLen = strlen(ext); 
    if (len < extLen) { 
     return false; 
    } 
    for (uint index = 1; index <= extLen; index++) { 
     if (filename[len - index] != ext[extLen - index]) { 
      return false; 
     } 
    } 
    return true; 
} 
0

rozwiązanie ogólne ze strlen (igła), strstr() i test dla "\ 0":

#include <stdio.h> 
#include <string.h> 
#include <stdbool.h> 

bool endsWith(const char* haystack, const char* needle) 
{ 
    bool rv = false; 
    if (haystack && needle) 
    { 
     size_t needle_size = strlen(needle); 
     const char* act = haystack; 
     while (NULL != (act = strstr(act, needle))) 
     { 
      if (*(act + needle_size) == '\0') 
      { 
       rv = true; 
       break; 
      } 
      act += needle_size; 
     } 
    } 

    return rv; 
} 

int main (int argc, char * argv[]) 
{ 
    char *a = "file1.gz"; 
    char *b = "1.gz"; 
    char *c = NULL; 
    char *d = "1.gzabc"; 
    char *e = "1.gzabc1.gz"; 

    printf("endsWith:\n"); 
    printf("%s %s = %d\n",a,b,endsWith(a,b)); 
    printf("%s NULL = %d\n",a,endsWith(a,c)); 
    printf("%s %s = %d\n",d,b,endsWith(d,b)); 
    printf("%s %s = %d\n",e,b,endsWith(e,b)); 

    return 0; 
} 
+0

Po zaniechaniu prosimy o komentarz. Moje rozwiązanie przynajmniej działa ... – lumpidu

0

Przepraszam, jestem trochę spóźniony na imprezę. Nie możesz zrobić czegoś z prostym wskaźnikiem matematyki?

char* str = "hello.foo"; //this would be string given 

int x = 4; //.foo has 4 characters 

int n = strlen(str)- x; //where x is equal to suffix length 

char* test = &str[n]; //do some pointer math to find the last characters 

if(strcmp(test, ".foo") == 0){ 
    //do some stuff 
}// end if 

Wskaźniki znaków działają, wskazując pierwszy znak w swojej tablicy. Więc kiedy to zrobisz, ustawisz pierwszy znak testu jako "." w ".foo" (jeśli to zawiera). Właśnie dlatego nie trzeba przydzielać pamięci dla niego, ponieważ wskazuje on tylko na już istniejącą tablicę znaków.

0
#include <assert.h> 
#include <string.h> 

int string_has_suffix(const char *str, const char *suf) 
{ 
    assert(str && suf); 

    const char *a = str + strlen(str); 
    const char *b = suf + strlen(suf); 

    while (a != str && b != suf) { 
     if (*--a != *--b) break; 
    } 
    return b == suf && *a == *b; 
} 

// Test Unit 
int main (int argc, char *argv[]) 
{ 
    assert(string_has_suffix("", "")); 
    assert(!string_has_suffix("", "a")); 
    assert(string_has_suffix("a", "")); 
    assert(string_has_suffix("a", "a")); 
    assert(!string_has_suffix("a", "b")); 
    assert(!string_has_suffix("a", "ba")); 
    assert(string_has_suffix("abc", "abc")); 
    assert(!string_has_suffix("abc", "eeabc")); 
    assert(!string_has_suffix("abc", "xbc")); 
    assert(!string_has_suffix("abc", "axc")); 
    assert(!string_has_suffix("abcdef", "abcxef")); 
    assert(!string_has_suffix("abcdef", "abxxef")); 
    assert(string_has_suffix("b.a", "")); 
    assert(string_has_suffix("b.a", "a")); 
    assert(string_has_suffix("b.a", ".a")); 
    assert(string_has_suffix("b.a", "b.a")); 
    assert(!string_has_suffix("b.a", "x")); 
    assert(string_has_suffix("abc.foo.bar", "")); 
    assert(string_has_suffix("abc.foo.bar", "r")); 
    assert(string_has_suffix("abc.foo.bar", "ar")); 
    assert(string_has_suffix("abc.foo.bar", "bar")); 
    assert(!string_has_suffix("abc.foo.bar", "xar")); 
    assert(string_has_suffix("abc.foo.bar", ".bar")); 
    assert(string_has_suffix("abc.foo.bar", "foo.bar")); 
    assert(!string_has_suffix("abc.foo.bar", "xoo.bar")); 
    assert(!string_has_suffix("abc.foo.bar", "foo.ba")); 
    assert(string_has_suffix("abc.foo.bar", ".foo.bar")); 
    assert(string_has_suffix("abc.foo.bar", "c.foo.bar")); 
    assert(string_has_suffix("abc.foo.bar", "abc.foo.bar")); 
    assert(!string_has_suffix("abc.foo.bar", "xabc.foo.bar")); 
    assert(!string_has_suffix("abc.foo.bar", "ac.foo.bar")); 
    assert(string_has_suffix("abc.foo.foo", ".foo")); 
    assert(string_has_suffix("abc.foo.foo", ".foo.foo")); 
    assert(string_has_suffix("abcdefgh", "")); 
    assert(!string_has_suffix("abcdefgh", " ")); 
    assert(string_has_suffix("abcdefgh", "h")); 
    assert(string_has_suffix("abcdefgh", "gh")); 
    assert(string_has_suffix("abcdefgh", "fgh")); 
    assert(!string_has_suffix("abcdefgh", "agh")); 
    assert(string_has_suffix("abcdefgh", "abcdefgh")); 

    return 0; 
} 

// $ gcc -Wall string_has_suffix.c && ./a.out 
0

Zawsze sprawdzam funkcje ciągów glib, mają one wszystkie przydatne bity. Funkcja kontroli przyrostków już istnieje.

gchar * str; 

if (!g_str_has_suffix(str)) { 
    return FALSE; 
} 

Jestem trochę nowy w C, więc przepraszam, jeśli to nie jest 100% ...ale wygląda na to, że mam do ciebie solidną klauzulę warty!