Я пытаюсь переопределить strcasecmp
функцию в C, и я заметил несоответствие в процессе сравнения.
От man strcmp
Функция strcmp () сравнивает две строки s1 и s2. Локаль не учитывается (сравнение с учетом локали смотрите в strcoll (3)). Он возвращает целое число, меньшее, равное или большее нуля, если найдено, что s1 соответственно меньше, соответствует или больше s2.
От man strcasecmp
Функция strcasecmp () выполняет побитовое сравнение строк s1 и s2, игнорируя регистр символов. Он возвращает целое число, меньшее, равное или большее нуля, если найдено, что s1 соответственно меньше, соответствует или больше s2.
int strcmp(const char *s1, const char *s2);
int strcasecmp(const char *s1, const char *s2);
Учитывая эту информацию, я не понимаю результат следующего кода:
#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
Похоже, что если текущий символ в s1
является буквой, он всегда преобразуется в нижний регистр, независимо от того, является ли текущий символ в s2
букве или нет.
Может кто-нибудь объяснить это поведение? Разве первая и третья строки не должны совпадать?
Заранее спасибо!
PS:
я использую gcc 9.2.0
на Манджаро.
Кроме того, когда я компилирую с -fno-builtin
флагом, я получаю вместо этого:
-30
2
2
2
Я думаю, это потому, что программа не использует оптимизированные функции gcc, но вопрос остается.
printf("%i\n", strcasecmp("a", "_"));
должен иметь тот же результат, что иprintf("%i\n", strcasecmp("A", "_"));
Но, это означает, что один из этих двух нечувствительных к регистру вызовов будет не согласен с его чувствительным к регистру аналогом.strcasecmp
которому вы обращаетесь, не является точным. Больше подробностей в ответах.A < _ && a > _ && A == a
, вызовет так много проблем.unsigned char
. C17 / 18 «Обработка строк <string.h>» -> «Для всех функций в этом подпункте каждый символ должен интерпретироваться так, как если бы он имел типunsigned char
». Это имеет значение, когдаchar
значения выходят за пределы диапазона ASCII 0-127.Ответы:
Поведение правильное.
Согласно спецификации POSIX
str\[n\]casecmp()
:Это также является частью в NOTES части страницы человека Linux :
Почему?
Как указал @HansOlsson в своем ответе , сравнение без учета регистра выполняется только для букв и позволяет всем другим сравнениям получать свои «естественные» результаты, как это сделано в
strcmp()
случае, нарушит сортировку.Если
'A' == 'a'
(определение сравнения без учета регистра), то'_' > 'A'
и'_' < 'a'
(«естественный» результат в наборе символов ASCII) не могут быть оба истинными.источник
'_' > 'A' && '_' < 'a'
; не похоже на лучший пример.'a' == 'A'
по определению , если сделать сравнение между «естественными» значениями'a'
,'A'
и'_
», вы не можете сделать регистронезависимое сравнение'A'
и'a'
получить равенство и получить устойчивые результаты сортировки.'a'
,'A'
и'_'
, пройдя все 6 порядков вставки в дерево, и сравнив результаты из указанных «всегда строчных букв» с предложенным вопросом «только преобразовывать буквы». когда это сравнение букв в буквы ". Например, используя последний алгоритм и начиная с'_'
,'a'
и'A'
сверните на противоположных сторонах дерева, но они определены как равные. «Конвертировать только буквы нижнего регистра в письме-буквенных сравнений» алгоритм разбит и те 3 символы показывают , что.'_' > 'A'
и'_' < 'a'
оба не могут быть правдой», не говоря нам, почему мы должны были когда-либо думать, что это будет. (Это задача для ответчика, а не для одного из миллионов читателей.)Другие ссылки, http://man7.org/linux/man-pages/man3/strcasecmp.3p.html для strcasecmp, говорят о том, что преобразование в нижний регистр - это правильное поведение (по крайней мере, в локали POSIX).
Причина такого поведения заключается в том, что если вы используете strcasecmp для сортировки массива строк, это необходимо для получения разумных результатов.
В противном случае, если вы попытаетесь отсортировать «A», «C», «_», «b» с помощью, например, qsort, результат будет зависеть от порядка сравнений.
источник
Это правильно - и это то, что должна делать
strcasecmp()
функция ! Это функция, а не часть Стандарта, но из « Базовых спецификаций открытой группы, выпуск 6 »:POSIX
C
Кстати, это поведение также имеет отношение к
_stricmp()
функции (как используется в Visual Studio / MSCV):источник
Десятичный код ASCII для for
A
предназначен65
для_
is95
и fora
is97
, поэтомуstrcmp()
он делает то, что должен делать. Лексикографически говоря_
меньшеa
и больше, чемA
.strcasecmp()
будет рассматриватьA
как являющийсяa
*, а так какa
больше , чем_
на выходе тоже правильно.* Стандарт POSIX.1-2008 говорит об этих функциях (strcasecmp () и strncasecmp ()):
Источник: http://man7.org/linux/man-pages/man3/strcasecmp.3.html
источник
A
он «больше», чем_
при сравнении без учета регистра, и удивляет, почему результат не такой, как при сравнении с учетом регистра.Since
strcasecmp () `нечувствителен к регистру, он будет рассматривать A как a`, что является недействительным выводом Процедура без учета регистра может обрабатывать все заглавные буквы, как если бы они были строчными буквами, могла обрабатывать все строчные буквы, как если бы они были заглавными буквами, или могла обрабатывать каждую заглавную букву как равную соответствующей строчной букве и наоборот, но все равно сравнивать их к буквенным символам с их необработанными значениями. В этом ответе не указывается причина предпочтения какой-либо из этих возможностей (правильная причина, по которой в документации сказано использовать строчные буквы).