Czy jest coś takiego startsWith(str_a, str_b)
w standardowej bibliotece C?
Powinien wziąć wskaźniki do dwóch ciągów kończących się na nullbajtach i powiedzieć mi, czy pierwszy z nich pojawia się również w całości na początku drugiego.
Przykłady:
"abc", "abcdef" -> true
"abcdef", "abc" -> false
"abd", "abdcef" -> true
"abc", "abc" -> true
c
string
comparison
startswith
thejh
źródło
źródło
Odpowiedzi:
Najwyraźniej nie ma do tego standardowej funkcji C. Więc:
bool startsWith(const char *pre, const char *str) { size_t lenpre = strlen(pre), lenstr = strlen(str); return lenstr < lenpre ? false : memcmp(pre, str, lenpre) == 0; }
Zauważ, że powyższe jest ładne i wyraźne, ale jeśli robisz to w ciasnej pętli lub pracujesz z bardzo dużymi strunami, nie oferuje najlepszej wydajności, ponieważ skanuje całą długość obu strun z przodu (
strlen
). Rozwiązania takie jak wj32 lub Christoph's mogą oferować lepsze wyniki (chociaż ten komentarz o wektoryzacji jest poza moim zasięgiem C). Należy również pamiętać, rozwiązanie Fred Foo który unikastrlen
onstr
(on ma rację, że to niepotrzebne, jeśli używaszstrncmp
zamiastmemcmp
). Ma znaczenie tylko w przypadku (bardzo) dużych strun lub wielokrotnego użycia w ciasnych pętlach, ale kiedy ma to znaczenie, ma znaczenie.źródło
memcmp
nastrncmp
tutaj i to szybciej. Nie ma UB, ponieważ wiadomo, że oba ciągi mają co najmniejlenpre
bajty.strncmp
sprawdza każdy bajt obu łańcuchów pod kątem wartości NUL, alestrlen
wywołania już gwarantowały, że ich nie ma. (Ale nadal ma wydajność, o której wspomniałeś, kiedypre
lubstr
jest dłuższa niż rzeczywista wspólna sekwencja początkowa.)memcmp
powyższego nie byłoby przywłaszczeniem z innej odpowiedzi tutaj, poszedłem dalej i zmieniłem to w odpowiedzi.strlen
imemcmp
może być zaimplementowana za pomocą bardzo szybkich instrukcji sprzętowych, astrlen
s mogą umieścić ciągi w pamięci podręcznej, unikając podwójnego trafienia w pamięć. Na takich maszynachstrncmp
można by zaimplementować jako dwastrlen
si amemcmp
tak po prostu, ale byłoby to ryzykowne dla osoby piszącej bibliotekę, ponieważ mogłoby to zająć znacznie więcej czasu w przypadku długich ciągów z krótkimi, wspólnymi prefiksami. Tutaj to trafienie jest wyraźne, astrlen
każde z nich jest wykonywane tylko raz (strlen
+ Fred Foostrncmp
zrobiłby 3).Nie ma do tego standardowej funkcji, ale możesz ją zdefiniować
bool prefix(const char *pre, const char *str) { return strncmp(pre, str, strlen(pre)) == 0; }
Nie musimy się martwić,
str
żepre
będziemy krótsi niż dlatego, że według standardu C (7.21.4.4/2):źródło
strncmp
.strncmp
istrlen
nie jest „nazywany strncmp”.Prawdopodobnie wybrałbym
strncmp()
, ale dla zabawy surowa implementacja:_Bool starts_with(const char *restrict string, const char *restrict prefix) { while(*prefix) { if(*prefix++ != *string++) return 0; } return 1; }
źródło
strncmp
, chyba że Twój kompilator jest naprawdę dobry w wektoryzacji, ponieważ pisarze glibc z pewnością są :-)Nie jestem ekspertem w pisaniu eleganckiego kodu, 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; }
źródło
Użyj
strstr()
funkcji.Stra == strstr(stra, strb)
źródło
Zoptymalizowany (v.2. - poprawiony):
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; }
źródło
startsWith("\2", "\1")
zwraca 1,startsWith("\1", "\1")
zwraca również 1Ponieważ uruchomiłem zaakceptowaną wersję i miałem problem z bardzo długim str, musiałem dodać następującą logikę:
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; }
źródło
Lub połączenie tych dwóch podejść:
_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)); } }
EDYCJA: Poniższy kod NIE działa, ponieważ jeśli strncmp zwraca 0, nie wiadomo, czy zostało osiągnięte kończące 0 lub długość (rozmiar_bloku).
Dodatkowym pomysłem jest porównanie blokowe. Jeśli blok nie jest równy, porównaj ten blok z oryginalną funkcją:
_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); string += block_size; prefix += block_size; if ( block_size < 4096 ) block_size *= 2; } }
Stałe
13
,64
,4096
, a także potęgowanie zblock_size
to tylko domysły. Musiałby zostać wybrany dla użytych danych wejściowych i sprzętu.źródło
block_size
inkrementacja musi nastąpić po inkrementacji wskaźnika. Teraz naprawione.