Próbuję ponownie wdrożyć strcasecmp
funkcję w C i zauważyłem, co wydaje się być niespójnością w procesie porównywania.
Od man strcmp
Funkcja strcmp () porównuje dwa ciągi s1 i s2. Ustawienia regionalne nie są brane pod uwagę (dla porównania uwzględniającego ustawienia regionalne, patrz strcoll (3)). Zwraca liczbę całkowitą mniejszą, równą lub większą od zera, jeśli okaże się, że s1 jest odpowiednio mniejsza niż, do dopasowania lub większa niż s2.
Od man strcasecmp
Funkcja strcasecmp () wykonuje porównanie bajtów po bajtach ciągów s1 i s2, ignorując wielkość liter w znakach. Zwraca liczbę całkowitą mniejszą, równą lub większą od zera, jeśli okaże się, że s1 jest odpowiednio mniejsza niż, do dopasowania lub większa niż s2.
int strcmp(const char *s1, const char *s2);
int strcasecmp(const char *s1, const char *s2);
Biorąc pod uwagę te informacje, nie rozumiem wyniku następującego kodu:
#include <stdio.h>
#include <string.h>
int main()
{
// ASCII values
// 'A' = 65
// '_' = 95
// 'a' = 97
printf("%i\n", strcmp("A", "_"));
printf("%i\n", strcmp("a", "_"));
printf("%i\n", strcasecmp("A", "_"));
printf("%i\n", strcasecmp("a", "_"));
return 0;
}
Ouput:
-1 # "A" is less than "_"
1 # "a" is more than "_"
2 # "A" is more than "_" with strcasecmp ???
2 # "a" is more than "_" with strcasecmp
Wygląda na to, że jeśli bieżącym znakiem w s1
jest litera, to zawsze jest konwertowany na małe litery, niezależnie od tego, czy bieżącym znakiem s2
jest litera, czy nie.
Czy ktoś może wyjaśnić to zachowanie? Czy pierwsza i trzecia linia nie powinny być identyczne?
Z góry dziękuję!
PS:
Używam gcc 9.2.0
na Manjaro.
Ponadto, kiedy kompiluję z -fno-builtin
flagą, otrzymuję zamiast tego:
-30
2
2
2
Chyba dlatego, że program nie korzysta ze zoptymalizowanych funkcji gcc, ale pozostaje pytanie.
printf("%i\n", strcasecmp("a", "_"));
Prawdopodobnie powinien on mieć taki sam wynik jakprintf("%i\n", strcasecmp("A", "_"));
Ale Oznacza to, że jedno z tych dwóch połączeń bez rozróżniania wielkości liter nie zgadza się z jego odpowiednikiem z rozróżnianiem wielkości liter.strcasecmp
którego się odnosisz, nie jest dokładny. Więcej szczegółów w recenzowanych odpowiedziach.A < _ && a > _ && A == a
, spowodowałaby tyle problemów.unsigned char
. C17 / 18 „Obsługa ciągów <ciąg.h>” -> „Dla wszystkich funkcji w niniejszej podsekcji każdy znak należy interpretować tak, jakby miał typunsigned char
”. To robi różnicę, gdychar
wartości są poza zakresem 0-127 ASCII.Odpowiedzi:
Zachowanie jest poprawne.
Zgodnie ze
str\[n\]casecmp()
specyfikacją POSIX :Który jest również częścią tej UWAGI sekcji strony Linux man :
Dlaczego?
Jak zauważył @HansOlsson w swojej odpowiedzi , dokonywanie bez rozróżniania wielkości liter porównań tylko liter i pozwalanie, aby wszystkie inne porównania miały swoje „naturalne” wyniki, tak jak w przypadku,
strcmp()
spowodowałoby przerwanie sortowania.Jeśli
'A' == 'a'
(definicja porównania bez rozróżniania wielkości liter), to'_' > 'A'
i'_' < 'a'
(„naturalny” wynik w zestawie znaków ASCII) nie mogą być prawdziwe.źródło
'_' > 'A' && '_' < 'a'
; nie wydaje się najlepszym przykładem.'a' == 'A'
z definicji , jeśli zrobić porównanie pomiędzy „naturalnych” wartości'a'
,'A'
i'_
”, to nie można zrobić bez uwzględniania wielkości liter porównanie'A'
i'a'
uzyskać równość i uzyskać spójne wyniki sortowania.'a'
,'A'
i'_'
, przechodząc przez wszystkie 6 rzędów wstawienie do drzewa, i porównując wyniki z AS-wymienionych „zawsze małe litery” na pytanie zaproponował „tylko konwertować litery gdy jest to porównanie liter do liter ". Na przykład, używając drugiego algorytmu i zaczynając od'_'
,'a'
i'A'
kończymy po przeciwnych stronach drzewa, ale są one zdefiniowane jako równe. Algorytm „konwertuj tylko litery na małe litery w porównaniach liter i liter” jest zepsuty i pokazują to 3 znaki.'_' > 'A'
i'_' < 'a'
nie może to być prawda”, nie mówiąc nam, dlaczego powinniśmy tak sądzić. (Jest to zadanie dla osoby odpowiadającej, a nie dla jednego z milionów czytelników.)Inne linki, http://man7.org/linux/man-pages/man3/strcasecmp.3p.html dla strcasecmp mówią, że konwersja na małe litery jest poprawnym zachowaniem (przynajmniej w ustawieniach regionalnych POSIX).
Powodem takiego zachowania jest to, że jeśli używasz strcasecmp do sortowania tablicy ciągów, potrzebne jest uzyskanie rozsądnych wyników.
W przeciwnym razie, jeśli spróbujesz posortować „A”, „C”, „_”, „b” przy użyciu np. Qsort, wynik będzie zależał od kolejności porównań.
źródło
Zgadza się - i taka powinna być
strcasecmp()
funkcja ! Jest to funkcja, a nie część standardu, ale z „ Podstawowych specyfikacji grupy otwartej, wydanie 6 ”:POSIX
C
Nawiasem mówiąc, to zachowanie dotyczy również
_stricmp()
funkcji (stosowanej w Visual Studio / MSCV):źródło
ASCII kod dziesiętny za
A
to65
za_
to95
i zaa
to97
, więcstrcmp()
to co robi to przypuszczać, aby zrobić. Mówiąc leksykograficznie,_
jest on wtedy mniejszya
i większy niżA
.strcasecmp()
będzieA
uważany zaa
*, a ponieważa
jest większy niż_
wynik jest również poprawny.* Standard POSIX.1-2008 mówi o tych funkcjach (strcasecmp () i strncasecmp ()):
Źródło: http://man7.org/linux/man-pages/man3/strcasecmp.3.html
źródło
A
jest „większy” niż_
przy porównywaniu bez rozróżniania wielkości liter i zastanawia się, dlaczego wynik nie jest taki sam, jak przy porównywaniu rozróżniania wielkości liter.Since
strcasecmp () `nie rozróżnia wielkości liter, uzna, że A jako a` jest niepoprawnym wnioskiem. Procedura bez rozróżniania wielkości liter może traktować wszystkie wielkie litery tak, jakby były małymi literami, może traktować wszystkie małe litery tak, jakby były dużymi literami, lub może traktować każdą wielką literę jako równą odpowiadającej jej małej i odwrotnie, ale nadal je porównywać do znaków innych niż litery z ich surowymi wartościami. Ta odpowiedź nie podaje powodu preferowania żadnej z tych możliwości (prawidłowy powód, dla którego dokumentacja mówi, aby używać małych liter).