Нечувствительная к регистру строка в C
У меня есть два почтовых индекса char*
, которые я хочу сравнить, игнорируя случай.
Есть ли функция для этого?
Или мне нужно прокручивать каждый из них, используя функцию tolower, а затем выполнить сравнение?
Любая идея, как эта функция будет реагировать на цифры в строке
Спасибо
Ответы
Ответ 1
В стандарте Си нет такой функции. Системы Unix, которые соответствуют POSIX, должны иметь strcasecmp
в заголовке strings.h
; Системы Microsoft имеют stricmp
. Чтобы быть на переносной стороне, напишите свое собственное:
int strcicmp(char const *a, char const *b)
{
for (;; a++, b++) {
int d = tolower((unsigned char)*a) - tolower((unsigned char)*b);
if (d != 0 || !*a)
return d;
}
}
Но обратите внимание, что ни одно из этих решений не будет работать со строками UTF-8, только ASCII.
Ответ 2
Посмотрите strcasecmp() в strings.h
.
Ответ 3
Я бы использовал stricmp()
. Он сравнивает две строки без учета случая.
Обратите внимание, что в некоторых случаях преобразование строки в нижний регистр может быть быстрее.
Ответ 4
Я нашел встроенный такой метод, из которого содержит дополнительные строковые функции для стандартного заголовка.
Здесь соответствующие подписи:
int strcasecmp(const char *, const char *);
int strncasecmp(const char *, const char *, size_t);
Я также нашел это синонимом в ядре xnu (osfmk/device/subrs.c), и он был реализован в следующем коде, поэтому вы не ожидали бы изменения поведения в количестве по сравнению с исходной функцией strcmp.
tolower(unsigned char ch) {
if (ch >= 'A' && ch <= 'Z')
ch = 'a' + (ch - 'A');
return ch;
}
int strcasecmp(const char *s1, const char *s2) {
const unsigned char *us1 = (const u_char *)s1,
*us2 = (const u_char *)s2;
while (tolower(*us1) == tolower(*us2++))
if (*us1++ == '\0')
return (0);
return (tolower(*us1) - tolower(*--us2));
}
Ответ 5
static int ignoreCaseComp (const char *str1, const char *str2, int length)
{
int k;
for (k = 0; k < length; k++)
{
if ((str1[k] | 32) != (str2[k] | 32))
break;
}
if (k != length)
return 1;
return 0;
}
Ссылка
Ответ 6
Дополнительные подводные камни, на которые стоит обратить внимание при выполнении сравнения без учета регистра:
Сравнивая как нижний или как верхний регистр? (достаточно распространенная проблема)
Оба ниже вернут 0 с strcicmpL("A", "a")
и strcicmpU("A", "a")
.
Тем не менее, strcicmpL("A", "_")
и strcicmpU("A", "_")
могут возвращать разные подписанные результаты, поскольку '_'
часто находится между прописными и строчными буквами.
Это влияет на порядок сортировки при использовании с qsort(...,...,..., strcicmp)
. Нестандартные функции библиотеки C, такие как общедоступные stricmp()
или strcasecmp()
имеют тенденцию быть хорошо определенными и предпочитают сравнивать через нижний регистр. Тем не менее вариации существуют.
int strcicmpL(char const *a, char const *b) {
while (*a) {
int d = tolower(*a) - tolower(*b);
if (d) {
return d;
}
a++;
b++;
}
return 0;
}
int strcicmpU(char const *a, char const *b) {
while (*a) {
int d = toupper(*a) - toupper(*b);
if (d) {
return d;
}
a++;
b++;
}
return 0;
}
char
может иметь отрицательное значение. (не редко)
touppper(int)
и tolower(int)
указываются для значений unsigned char
и отрицательного EOF
. Кроме того, strcmp()
возвращает результаты, как если бы каждый char
был преобразован в unsigned char
, независимо от того, является ли char
подписанным или без знака.
tolower(*a); // Potential UB
tolower((unsigned char) *a); // Correct
Язык (менее распространенный)
Хотя наборы символов, использующие код ASCII (0-127), встречаются повсеместно, остальные коды, как правило, имеют специфичные для локали проблемы. Поэтому strcasecmp("\xE4", "a")
может вернуть 0 в одной системе и ненулевое значение в другой.
Юникод (путь будущего)
Если решение должно обрабатывать больше, чем ASCII, рассмотрите unicode_strcicmp()
. Поскольку C lib не предоставляет такой функции, рекомендуется предварительно закодированная функция из некоторой альтернативной библиотеки. Написание своего собственного unicode_strcicmp()
является сложной задачей.
Все ли буквы отображают одну нижнюю на одну верхнюю? (педантичный)
[AZ] сопоставляет один-к-одному с [az], но различные локали отображают различные символы нижнего регистра в один верхний и наоборот. Кроме того, некоторые символы в верхнем регистре могут не иметь эквивалента в нижнем регистре и, наоборот, наоборот.
Это обязывает код проходить через tolower()
и tolower()
.
int d = tolower(toupper(*a)) - tolower(toupper(*b));
Опять же, потенциально разные результаты для сортировки, если код сделал tolower(toupper(*a))
против toupper(tolower(*a))
.
портативность
@B. Надолсон рекомендует избегать использования собственного strcicmp()
и это разумно, за исключением случаев, когда коду требуется высокая эквивалентная переносимая функциональность.
Ниже представлен подход, который даже выполнялся быстрее, чем некоторые функции, предоставляемые системой. Он выполняет одно сравнение в цикле, а не в двух, используя две разные таблицы, которые отличаются от '\0'
. Ваши результаты могут отличаться.
static unsigned char low1[UCHAR_MAX + 1] = {
0, 1, 2, 3, ...
'@', 'a', 'b', 'c', ... 'z', '[', ... // @ABC... Z[...
''', 'a', 'b', 'c', ... 'z', '{', ... // 'abc... z{...
}
static unsigned char low2[UCHAR_MAX + 1] = {
// v--- Not zero, but A which matches none in 'low1[]'
'A', 1, 2, 3, ...
'@', 'a', 'b', 'c', ... 'z', '[', ...
''', 'a', 'b', 'c', ... 'z', '{', ...
}
int strcicmp_ch(char const *a, char const *b) {
// compare using tables that differ slightly.
while (low1[(unsigned char)*a] == low2[(unsigned char)*b]) {
a++;
b++;
}
// Either strings differ or null character detected.
// Perform subtraction using same table.
return (low1[(unsigned char)*a] - low1[(unsigned char)*b]);
}
Ответ 7
Вы можете получить представление о том, как реализовать эффективный, если у вас его нет в библиотеке, здесь
Он использует таблицу для всех 256 символов.
- в этой таблице для всех символов, кроме букв - используется его ascii-коды.
- для буквенных кодов верхнего регистра - коды списка таблиц с нижними обложенными символами.
тогда нам просто нужно пересечь строки и сравнить наши ячейки таблицы для заданных символов:
const char *cm = charmap,
*us1 = (const char *)s1,
*us2 = (const char *)s2;
while (cm[*us1] == cm[*us2++])
if (*us1++ == '\0')
return (0);
return (cm[*us1] - cm[*--us2]);
Ответ 8
Я на самом деле не фанат ответа с наибольшим количеством голосов здесь (отчасти потому, что он неправильный, поскольку он должен продолжаться, если он читает нулевой терминатор в любой строке - но не в обеих строках одновременно - и это не ' не делаю этого), поэтому я написал свой.
Это прямая замена strncmp()
, которая была полностью протестирована с многочисленными тестами, как показано ниже:
Только код:
#include <ctype.h> // for 'tolower()'
#include <limits.h> // for 'INT_MIN'
// Case-insensitive 'strncmp()'
static inline int strncmpci(const char * str1, const char * str2, size_t num)
{
int ret_code = INT_MIN;
size_t chars_compared = 0;
if (!str1 || !str2)
{
goto done;
}
while ((*str1 || *str2) && (chars_compared < num))
{
ret_code = tolower((int)(*str1)) - tolower((int)(*str2));
if (ret_code != 0)
{
break;
}
chars_compared++;
str1++;
str2++;
}
done:
return ret_code;
}
Полностью прокомментированная версия:
#include <ctype.h> // for 'tolower()'
#include <limits.h> // for 'INT_MIN'
/*
Case-insensitive string compare (strncmp case-insensitive)
- Identical to strncmp except case-insensitive. See: http://www.cplusplus.com/reference/cstring/strncmp/
- Aided/inspired, in part, by: /questions/138783/case-insensitive-string-comp-in-c/825030#825030
str1 C string 1 to be compared
str2 C string 2 to be compared
num max number of chars to compare
return:
(essentially identical to strncmp)
INT_MIN invalid arguments (one or both of the input strings is a NULL pointer)
<0 the first character that does not match has a lower value in str1 than in str2
0 the contents of both strings are equal
>0 the first character that does not match has a greater value in str1 than in str2
*/
static inline int strncmpci(const char * str1, const char * str2, size_t num)
{
int ret_code = INT_MIN;
size_t chars_compared = 0;
// Check for NULL pointers
if (!str1 || !str2)
{
goto done;
}
// Continue doing case-insensitive comparisons, one-character-at-a-time, of str1 to str2,
// as long as at least one of the strings still has more characters in it, and we have
// not yet compared num chars.
while ((*str1 || *str2) && (chars_compared < num))
{
ret_code = tolower((int)(*str1)) - tolower((int)(*str2));
if (ret_code != 0)
{
// The 2 chars just compared don't match
break;
}
chars_compared++;
str1++;
str2++;
}
done:
return ret_code;
}
int main()
{
printf("Hello World\n\n");
const char * str1;
const char * str2;
size_t n;
str1 = "hey";
str2 = "HEY";
n = 3;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
str1 = "heY";
str2 = "HeY";
n = 3;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
str1 = "hey";
str2 = "HEdY";
n = 3;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
str1 = "heY";
str2 = "HeYd";
n = 3;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
str1 = "heY";
str2 = "HeYd";
n = 6;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
str1 = "hey";
str2 = "hey";
n = 6;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
str1 = "hey";
str2 = "heyd";
n = 6;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
str1 = "hey";
str2 = "heyd";
n = 3;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
return 0;
}
Образец вывода:
Привет, мир
strncmpci (эй, эй, 3) = 0
strncmp (эй, эй, 3) = 32
strncmpci (heY, HeY, 3) = 0
strncmp (heY, HeY, 3) = 32
strncmpci (hey, HEdY, 3) = 21
strncmp (эй, HEdY, 3) = 32
strncmpci (heY, HeYd, 3) = 0
strncmp (heY, HeYd, 3) = 32
strncmpci (heY, HeYd, 6) = -100
strncmp (heY, HeYd, 6) = 32
strncmpci (эй, эй, 6) = 0
strncmp (эй, эй, 6) = 0
strncmpci (эй, хейд, 6) = -100
strncmp (эй, хейд, 6) = -100
strncmpci (эй, хейд, 3) = 0
strncmp (эй, хейд, 3) = 0
Рекомендации:
- Этот вопрос и другие ответы здесь послужили вдохновением и дали некоторое представление (регистр нечувствительных к регистру строк в C)
- http://www.cplusplus.com/reference/cstring/strncmp/
- https://en.wikipedia.org/wiki/ASCII
- https://en.cppreference.com/w/c/language/operator_precedence
Ответ 9
Как уже говорили другие, не существует переносимой функции, которая работает во всех системах. Вы можете частично обойти это с помощью простого ifdef
:
#include <stdio.h>
#ifdef _WIN32
#include <string.h>
#define strcasecmp _stricmp
#else // assuming POSIX or BSD compliant system
#include <strings.h>
#endif
int main() {
printf("%d", strcasecmp("teSt", "TEst"));
}
Ответ 10
int strcmpInsensitive(char* a, char* b)
{
return strcmp(lowerCaseWord(a), lowerCaseWord(b));
}
char* lowerCaseWord(char* a)
{
char *b=new char[strlen(a)];
for (int i = 0; i < strlen(a); i++)
{
b[i] = tolower(a[i]);
}
return b;
}
удача
Функция Edit-lowerCaseWord получает переменную char * с и возвращает значение нижнего регистра этого char *. Например, "AbCdE" для значения char *, вернет "abcde".
В основном, он принимает две переменные char * после передачи в нижний регистр и использует для них функцию strcmp.
Например, если мы вызываем функцию strcmpInsensitive для значений "AbCdE" и "ABCDE", она сначала возвращает оба значения в нижнем регистре ( "abcde" ), а затем выполняет функцию strcmp на них.