Регистр нечувствительных к регистру строк в C - PullRequest
58 голосов
/ 28 апреля 2011

У меня есть два почтовых индекса char*, которые я хочу сравнить, игнорируя регистр.Есть ли функция, чтобы сделать это?

Или я должен циклически проходить через каждое использование функции tolower, а затем делать сравнение?

Любая идея, как эта функция будет реагировать с числами в строке

Спасибо

Ответы [ 10 ]

55 голосов
/ 28 апреля 2011

Нет функции, которая делает это в стандарте C.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.

31 голосов
/ 28 апреля 2011

Посмотрите на strcasecmp () in strings.h.

4 голосов
/ 04 января 2016

Я нашел встроенный такой метод, названный, который содержит дополнительные строковые функции к стандартному заголовку.

Вот соответствующие подписи:

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));
}
4 голосов
/ 28 апреля 2011

Я бы использовал stricmp(). Он сравнивает две строки без учета регистра.

Обратите внимание, что в некоторых случаях преобразование строки в нижний регистр может быть быстрее.

2 голосов
/ 23 августа 2018

Дополнительные подводные камни, на которые следует обратить внимание при выполнении сравнения без учета регистра:


Сравнение в нижнем или верхнем регистре?(достаточно распространенная проблема)

Оба приведенных ниже вернут 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 в одной системе и ненулевое значение в другой.


Unicode (путь в будущее)

Если решениенеобходимо обработать больше, чем 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.Nadolson рекомендует избегать использования собственных strcicmp(), и это разумно, за исключением случаев, когда для кода требуется высокая эквивалентная переносимая функциональность.

Ниже приведен подход, который даже выполнялся быстрее, чем некоторые функции, предоставляемые системой.Он выполняет одно сравнение на цикл, а не два, используя 2 разные таблицы, которые отличаются от '\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]);
}
0 голосов
/ 23 мая 2019

Как уже говорили другие, не существует переносимой функции, которая работает во всех системах. Вы можете частично обойти это простым 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"));
}
0 голосов
/ 22 марта 2019

Я на самом деле не фанат ответа с наибольшим количеством голосов здесь (отчасти потому, что он неправильный, так как он должен продолжаться, если он читает нулевой терминатор в любой строке - но не обаСтруны сразу - и это не делает этого), поэтому я написал свой собственный.

Это прямая замена для 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: https://stackoverflow.com/a/5820991/4561887

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;
}

Тестовый код: (запустите его здесь): https://onlinegdb.com/B1Qoj0W_N

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;
}

Пример вывода:

Hello World

strncmpci (hey, HEY, 3) = 0
strncmp (hey, HEY, 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 (hey, hey, 6) = 0
strncmp (hey, hey, 6) = 0

strncmpci (хей, хейд, 6) = -100
strncmp (хей, хейд, 6) = -100

strncmpci (хей, хейд, 3) = 0
strncmp (хей, хейд, 3) = 0

Ссылки:

  1. Этот вопрос и другие ответы здесь послужили источником вдохновения и дали некоторое представление ( Компонент без учета регистра в C )
  2. http://www.cplusplus.com/reference/cstring/strncmp/
  3. https://en.wikipedia.org/wiki/ASCII
  4. https://en.cppreference.com/w/c/language/operator_precedence
0 голосов
/ 14 февраля 2016
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;
}

Ссылка

0 голосов
/ 22 января 2016
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 * и возвращает значение нижнего регистра этого символа *. Например, «AbCdE» для значения char *, вернет «abcde».

По сути, он берет две переменные char * после перевода в нижний регистр и использует для них функцию strcmp.

Например, если мы вызываем функцию strcmpInsensitive для значений «AbCdE» и «ABCDE», она сначала возвращает оба значения в нижнем регистре («abcde»), а затем выполняет для них функцию strcmp.

0 голосов
/ 27 декабря 2015

Вы можете получить представление о том, как реализовать эффективный, если у вас его нет в библиотеке, из здесь

В нем используется таблица для всех 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]);
...