Сравнение строк без учета регистра в C ++ [закрыто]

Каков наилучший способ сравнения строк без учета регистра в C ++ без преобразования строки в верхний или нижний регистр?

Пожалуйста, укажите, являются ли методы удобными для Unicode и насколько они переносимы.

14.08.2008 20:01:28
@ [Адам] (# 11679): Хотя этот вариант хорош с точки зрения удобства использования, он плох с точки зрения производительности, поскольку создает ненужные копии. Я мог бы что-то упустить, но я считаю, что лучший (не Unicode) способ это использовать std::stricmp. В противном случае, прочитайте, что Херб должен сказать .
Konrad Rudolph 26.08.2008 12:17:08
В случае c обычно нужно перебирать всю строку, а затем сравнивать таким образом - или бросать собственное сравнение: P
Michael Dorgan 22.05.2010 01:10:38
более поздний вопрос имеет более простой ответ: strcasecmp (по крайней мере для компиляторов BSD & POSIX) stackoverflow.com/questions/9182912/…
Móż 5.11.2013 21:39:48
@ Mσᶎ на этот вопрос также есть ответ, с важным предупреждением, strcasecmpкоторое не является частью стандарта и отсутствует по крайней мере в одном общем компиляторе.
Mark Ransom 1.12.2014 19:57:07
30 ОТВЕТОВ
РЕШЕНИЕ

Boost включает в себя удобный алгоритм для этого:

#include <boost/algorithm/string.hpp>
// Or, for fewer header dependencies:
//#include <boost/algorithm/string/predicate.hpp>

std::string str1 = "hello, world!";
std::string str2 = "HELLO, WORLD!";

if (boost::iequals(str1, str2))
{
    // Strings are identical
}
316
22.06.2012 18:28:45
Это UTF-8 дружественный? Думаю, нет.
vladr 30.10.2010 00:23:11
Нет, потому что UTF-8 позволяет кодировать идентичные строки с разными двоичными кодами из-за акцентов, объединений, проблем с биди и т. Д.
vy32 18.06.2011 23:35:22
@ vy32 Это абсолютно неверно! Комбинации UTF-8 являются взаимоисключающими. Он всегда должен использовать кратчайшее возможное представление, если это не так, то это неправильно сформированная последовательность UTF-8 или кодовая точка, к которой следует обращаться с осторожностью.
Wiz 10.11.2011 23:44:41
@Wiz, вы игнорируете проблему нормализации строки Unicode. ñ может быть представлен как комбинация ˜, за которой следует n или символ ñ. Вам необходимо использовать нормализацию строки Unicode перед выполнением сравнения. Пожалуйста , ознакомьтесь с Unicode Технический отчет № 15, unicode.org/reports/tr15
vy32 11.11.2011 03:21:09
@wonkorealtime: потому что "ß", преобразованный в верхний регистр, - это "SS": fileformat.info/info/unicode/char/df/index.htm
Mooing Duck 29.05.2014 23:11:44

Предполагая, что вы ищете метод, а не волшебную функцию, которая уже существует, откровенно говоря, лучшего способа нет. Мы все могли бы написать фрагменты кода с умными трюками для ограниченного набора символов, но в конце дня в какой-то момент вы должны преобразовать символы.

Лучший подход для этого преобразования - сделать это до сравнения. Это дает вам большую гибкость, когда речь идет о схемах кодирования, о которых ваш действительный оператор сравнения должен не знать.

Конечно, вы можете «скрыть» это преобразование за своей собственной строковой функцией или классом, но вам все равно нужно преобразовать строки перед сравнением.

6
14.08.2008 20:13:45

Строковые функции Visual C ++, поддерживающие Юникод: http://msdn.microsoft.com/en-us/library/cc194799.aspx

тот, который вы, вероятно, ищете _wcsnicmp

14
26.07.2013 15:17:11
По иронии судьбы, «широкие символьные коды» Microsoft НЕ чисты в Юникоде, потому что они не обрабатывают нормализацию Юникода.
vy32 18.06.2011 23:36:14

К вашему сведению, strcmp()и stricmp()уязвимы для переполнения буфера, так как они просто обрабатывают, пока не достигнут нулевого терминатора. Безопаснее использовать _strncmp()и _strnicmp().

11
26.07.2013 15:18:45
Правда, хотя перезапись буфера значительно менее опасна, чем перезапись буфера.
Adam Rosenfield 17.11.2008 20:47:34
stricmp()и strnicmp()не являются частью стандарта POSIX :-( Однако вы можете найти strcasecmp(), strcasecmp_l(), strncasecmp()и strncasecmp_l()в заголовке POSIX strings.h:-) см opengroup.org
olibre 11.04.2013 12:27:35
@AdamRosenfield «хуже» зависит от контекста. В области безопасности иногда весь смысл перезаписи заключается в том, чтобы перезаписать.
karmakaze 21.03.2015 16:22:32

У меня был хороший опыт использования библиотек International Components for Unicode - они чрезвычайно мощные и предоставляют методы для преобразования, поддержки локали, рендеринга даты и времени, отображения дел (что вам не нужно) и сопоставления , который включает сравнение без учета регистра и акцента (и многое другое). Я использовал только C ++-версию библиотек, но, похоже, они также имеют Java-версию.

Существуют методы для выполнения нормализованных сравнений, на которые ссылается @Coincoin, и они могут даже учитывать локаль - например (и это пример сортировки, а не строго равенство), традиционно на испанском (в Испании) сочетание букв «ll» сортирует между "l" и "m", поэтому "lz" <"ll" <"ma".

4
26.08.2008 11:02:19

Вы говорите о немом сравнении без учета регистра или полном нормализованном сравнении Unicode?

При немом сравнении не будут найдены строки, которые могут быть одинаковыми, но не являются двоичными.

Пример:

U212B (ANGSTROM SIGN)
U0041 (LATIN CAPITAL LETTER A) + U030A (COMBINING RING ABOVE)
U00C5 (LATIN CAPITAL LETTER A WITH RING ABOVE).

Все они эквивалентны, но они также имеют разные двоичные представления.

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

Кроме того, IBM в значительной степени запатентовала наиболее оптимизированные алгоритмы Unicode и сделала их общедоступными. Они также поддерживают реализацию: IBM ICU

53
13.04.2020 11:53:59
Вы можете отредактировать эту ссылку ICU на site.icu-project.org
DevSolar 13.12.2017 13:15:45

Я пытаюсь собрать хороший ответ из всех постов, поэтому помогите мне отредактировать это:

Вот способ сделать это, хотя он преобразует строки и не поддерживает Unicode, он должен быть переносимым, что является плюсом:

bool caseInsensitiveStringCompare( const std::string& str1, const std::string& str2 ) {
    std::string str1Cpy( str1 );
    std::string str2Cpy( str2 );
    std::transform( str1Cpy.begin(), str1Cpy.end(), str1Cpy.begin(), ::tolower );
    std::transform( str2Cpy.begin(), str2Cpy.end(), str2Cpy.begin(), ::tolower );
    return ( str1Cpy == str2Cpy );
}

Из того, что я прочитал, это более переносимо, чем stricmp (), потому что stricmp () на самом деле не является частью библиотеки std, а реализуется только большинством поставщиков компиляторов.

Чтобы получить действительно Unicode-дружественную реализацию, кажется, вы должны выйти за пределы библиотеки std. Одна хорошая сторонняя библиотека - IBM ICU (Международные компоненты для Unicode)

Также boost :: iequals предоставляет довольно хорошую утилиту для такого рода сравнения.

13
19.12.2011 21:54:29
Подскажите, пожалуйста, что означает :: tolower, почему вы можете использовать tolower вместо tolower () и что такое «::» раньше? спасибо
VextoR 11.03.2011 08:40:07
Это не очень эффективное решение - вы копируете обе строки и трансформируете их все, даже если первый символ отличается.
Timmmm 13.03.2011 18:14:41
Если вы все равно собираетесь сделать копию, почему бы не передать по значению, а не по ссылке?
celticminstrel 21.06.2015 02:17:50
Я думаю, что это простой совет без повышения. :)
cmcromance 8.11.2016 08:33:17
вопрос задает явно не transformвсю строку перед сравнением
Sandburg 6.06.2019 15:43:10

Если вы работаете в системе POSIX, вы можете использовать strcasecmp . Однако эта функция не является частью стандартного C и не доступна в Windows. Это будет выполнять сравнение без учета регистра для 8-битных символов, при условии, что языковой стандарт POSIX. Если языковой стандарт не POSIX, результаты не определены (поэтому может выполняться локальное сравнение или нет). Эквивалент с широкими символами недоступен.

В противном случае большое количество исторических реализаций библиотеки C имеют функции stricmp () и strnicmp (). Visual C ++ в Windows переименовал все это, поставив перед ними подчеркивание, потому что они не являются частью стандарта ANSI, поэтому в этой системе они называются _stricmp или _strnicmp . Некоторые библиотеки могут также иметь широко-символьные или многобайтовые эквивалентные функции (обычно называемые, например, wcsicmp, mbcsicmp и т. Д.).

C и C ++ в значительной степени не знают о проблемах интернационализации, поэтому у этой проблемы нет иного решения, кроме как использовать стороннюю библиотеку. Проверьте IBM ICU (Международные компоненты для Unicode), если вам нужна надежная библиотека для C / C ++. ICU для систем Windows и Unix.

58
4.06.2015 14:13:48

Моей первой мыслью для не-Unicode-версии было сделать что-то вроде этого:


bool caseInsensitiveStringCompare(const string& str1, const string& str2) {
    if (str1.size() != str2.size()) {
        return false;
    }
    for (string::const_iterator c1 = str1.begin(), c2 = str2.begin(); c1 != str1.end(); ++c1, ++c2) {
        if (tolower(*c1) != tolower(*c2)) {
            return false;
        }
    }
    return true;
}
30
26.08.2008 11:51:28

Я написал версию char_traits без учета регистра для использования с std :: basic_string, чтобы сгенерировать std :: string, которая не учитывает регистр при сравнении, поиске и т. Д. С использованием встроенных функций-членов std :: basic_string.

Другими словами, я хотел сделать что-то подобное.

std::string a = "Hello, World!";
std::string b = "hello, world!";

assert( a == b );

... который std :: string не может обработать Вот использование моего нового char_traits:

std::istring a = "Hello, World!";
std::istring b = "hello, world!";

assert( a == b );

... и вот реализация:

/*  ---

        Case-Insensitive char_traits for std::string's

        Use:

            To declare a std::string which preserves case but ignores case in comparisons & search,
            use the following syntax:

                std::basic_string<char, char_traits_nocase<char> > noCaseString;

            A typedef is declared below which simplifies this use for chars:

                typedef std::basic_string<char, char_traits_nocase<char> > istring;

    --- */

    template<class C>
    struct char_traits_nocase : public std::char_traits<C>
    {
        static bool eq( const C& c1, const C& c2 )
        { 
            return ::toupper(c1) == ::toupper(c2); 
        }

        static bool lt( const C& c1, const C& c2 )
        { 
            return ::toupper(c1) < ::toupper(c2);
        }

        static int compare( const C* s1, const C* s2, size_t N )
        {
            return _strnicmp(s1, s2, N);
        }

        static const char* find( const C* s, size_t N, const C& a )
        {
            for( size_t i=0 ; i<N ; ++i )
            {
                if( ::toupper(s[i]) == ::toupper(a) ) 
                    return s+i ;
            }
            return 0 ;
        }

        static bool eq_int_type( const int_type& c1, const int_type& c2 )
        { 
            return ::toupper(c1) == ::toupper(c2) ; 
        }       
    };

    template<>
    struct char_traits_nocase<wchar_t> : public std::char_traits<wchar_t>
    {
        static bool eq( const wchar_t& c1, const wchar_t& c2 )
        { 
            return ::towupper(c1) == ::towupper(c2); 
        }

        static bool lt( const wchar_t& c1, const wchar_t& c2 )
        { 
            return ::towupper(c1) < ::towupper(c2);
        }

        static int compare( const wchar_t* s1, const wchar_t* s2, size_t N )
        {
            return _wcsnicmp(s1, s2, N);
        }

        static const wchar_t* find( const wchar_t* s, size_t N, const wchar_t& a )
        {
            for( size_t i=0 ; i<N ; ++i )
            {
                if( ::towupper(s[i]) == ::towupper(a) ) 
                    return s+i ;
            }
            return 0 ;
        }

        static bool eq_int_type( const int_type& c1, const int_type& c2 )
        { 
            return ::towupper(c1) == ::towupper(c2) ; 
        }       
    };

    typedef std::basic_string<char, char_traits_nocase<char> > istring;
    typedef std::basic_string<wchar_t, char_traits_nocase<wchar_t> > iwstring;
6
17.11.2008 23:32:10
Это работает для обычных символов, но не будет работать для всего Unicode, поскольку капитализация не обязательно является двунаправленной (есть хороший пример греческого использования сигмы, который я не могу вспомнить сейчас; что-то вроде этого имеет два нижних и один верхний регистр , и вы не можете получить надлежащее сравнение в любом случае)
coppro 24.11.2008 21:02:24
Это действительно неправильный путь. Чувствительность к регистру не должна быть свойством самих строк. Что происходит, когда один и тот же строковый объект нуждается в сравнении с учетом регистра и без учета регистра?
Ferruccio 24.11.2008 21:07:58
Если чувствительность к регистру не подходит для того, чтобы быть «частью» строки, тогда ни одна из них не является функцией find (). Что для вас может быть правдой, и это нормально. ИМО самое главное в C ++ - это то, что он не навязывает программисту определенную парадигму. Это то, что вы хотите / нужно, чтобы это было.
John Dibling 25.11.2008 06:20:21
На самом деле, я думаю, что большинство C ++ - гуру (как и те, что в комитете по стандартам) согласны с тем, что было ошибкой помещать find () в std :: basic_string <> вместе со множеством других вещей, которые с таким же успехом можно было бы поместить в свободные функции. Кроме того, есть некоторые проблемы с помещением этого типа.
Andreas Magnusson 25.11.2008 07:50:29
Как уже отмечали другие, в этом решении есть две основные проблемы (по иронии судьбы, одна - это интерфейс, а другая - реализация ;-)).
Konrad Rudolph 25.11.2008 08:15:48

Просто заметка о том, какой метод вы в конечном итоге выберете, если этот метод включает использование того, strcmpчто некоторые ответы предлагают:

strcmpне работает с данными Unicode в целом. В общем, он даже не работает с байтовыми кодировками Unicode, такими как utf-8, поскольку strcmpтолько сравнение байтов за байтом, а кодовые точки Unicode, закодированные в utf-8, могут занимать более 1 байта. Единственный конкретный случай Unicode, который strcmpправильно обрабатывается, - это когда строка, закодированная с помощью байтовой кодировки, содержит только кодовые точки ниже U + 00FF - тогда достаточно сравнения байтов на байты.

2
25.11.2008 07:26:28

Вы можете использовать strcasecmpна Unix или stricmpWindows.

Одна вещь, о которой пока не упоминалось, это то, что если вы используете строки stl с этими методами, полезно сначала сравнить длину двух строк, так как эта информация уже доступна вам в классе строк. Это может помешать проведению дорогостоящего сравнения строк, если сравниваемые две строки имеют даже разную длину.

20
26.07.2013 15:17:59
Поскольку определение длины строки состоит из итерации по каждому символу в строке и сравнения ее с 0, действительно ли так много различий между ними и сравнение строк сразу? Я предполагаю, что вы получаете лучшую локальность памяти в случае, когда обе строки не совпадают, но, вероятно, почти в 2 раза больше времени выполнения в случае совпадения.
uliwitness 22.01.2014 13:28:37
C ++ 11 определяет, что сложность std :: string :: length должна быть постоянной: cplusplus.com/reference/string/string/length
bradtgmurray 4.02.2014 21:37:11
Это забавный маленький факт, но здесь мало что значит. strcasecmp () и stricmp () принимают недекорированные строки C, поэтому в них не используется std :: string.
uliwitness 5.02.2014 17:39:18
Эти методы вернут -1, если вы сравните «a» с «ab». Длина различна, но «а» предшествует «а». Таким образом, простое сравнение длин не представляется возможным, если вызывающая сторона заботится о заказе.
Nathan 12.02.2014 23:33:05

В библиотеке Boost.String имеется множество алгоритмов для сравнения без учета регистра и так далее.

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

11
22.05.2010 00:57:43
Нет способа, встроенного в std :: string?
WilliamKF 22.05.2010 00:58:51
Нет, нет
Dean Harding 22.05.2010 01:06:59
"... зачем, когда это уже сделано?" - что делать, если вы не используете Boost? У ОП не было тега с вопросом.
jww 7.02.2016 22:07:44

Воспользуйтесь преимуществом стандарта char_traits. Напомним , что std::stringэто на самом деле ЬурейеЕ для std::basic_string<char>или более явно, std::basic_string<char, std::char_traits<char> >. char_traitsТип описывает , как символы сравнить, как они копируют, как они бросают и т.д. Все , что нужно сделать , это ЬурейеЕ новую строку над basic_string, и предоставить его своим обычаем , char_traitsчто сравнить случай нечувствительно.

struct ci_char_traits : public char_traits<char> {
    static bool eq(char c1, char c2) { return toupper(c1) == toupper(c2); }
    static bool ne(char c1, char c2) { return toupper(c1) != toupper(c2); }
    static bool lt(char c1, char c2) { return toupper(c1) <  toupper(c2); }
    static int compare(const char* s1, const char* s2, size_t n) {
        while( n-- != 0 ) {
            if( toupper(*s1) < toupper(*s2) ) return -1;
            if( toupper(*s1) > toupper(*s2) ) return 1;
            ++s1; ++s2;
        }
        return 0;
    }
    static const char* find(const char* s, int n, char a) {
        while( n-- > 0 && toupper(*s) != toupper(a) ) {
            ++s;
        }
        return s;
    }
};

typedef std::basic_string<char, ci_char_traits> ci_string;

Подробности на Гуру Недели № 29 .

117
22.05.2010 02:16:31
Насколько я знаю из моих собственных экспериментов, это делает ваш новый строковый тип несовместимым с std :: string.
Zan Lynx 26.09.2012 21:25:20
Конечно, это - для его же блага. Строка без учета регистра есть нечто другое: typedef std::basic_string<char, ci_char_traits<char> > istringне typedef std::basic_string<char, std::char_traits<char> > string.
Andreas Spindler 9.10.2012 09:24:33
«Все, что вам нужно сделать ...»
Tim MB 19.04.2013 10:03:42
@ Натан, вероятно, использует компилятор, который способен выполнять базовые CSE на коде ...
The Paramagnetic Croissant 12.10.2014 07:41:02
Любая языковая конструкция, которая вызывает такое безумие в этом тривиальном случае, должна быть и может быть оставлена ​​без сожалений.
Erik Aronesty 14.11.2014 14:17:54

Проблема с бустом заключается в том, что вы должны связываться с ним и зависеть от него. Не легко в некоторых случаях (например, Android).

А использование char_traits означает, что все ваши сравнения не чувствительны к регистру, а это обычно не то, что вам нужно.

Этого должно быть достаточно. Это должно быть достаточно эффективным. Не обрабатывает Unicode или что-то еще, хотя.

bool iequals(const string& a, const string& b)
{
    unsigned int sz = a.size();
    if (b.size() != sz)
        return false;
    for (unsigned int i = 0; i < sz; ++i)
        if (tolower(a[i]) != tolower(b[i]))
            return false;
    return true;
}

Обновление: Бонус C ++ 14 версия ( #include <algorithm>):

bool iequals(const string& a, const string& b)
{
    return std::equal(a.begin(), a.end(),
                      b.begin(), b.end(),
                      [](char a, char b) {
                          return tolower(a) == tolower(b);
                      });
}
85
28.09.2019 06:53:56
На самом деле, библиотека String String - это библиотека только для заголовков, поэтому нет необходимости ссылаться на что-либо. Кроме того, вы можете использовать утилиту boost 'bcp', чтобы скопировать только заголовки строк в дерево исходных текстов, поэтому вам не требуется полная библиотека boost.
Gretchen 9.03.2011 21:47:38
Ах, я не знал о BCP, это выглядит действительно полезным. Спасибо за информацию!
Timmmm 13.03.2011 18:15:48
Полезно знать простую версию без зависимости от буста.
Deqing 17.05.2014 03:31:02
@ Анна Текстовая библиотека Boost должна быть построена и ссылка. Он использует IBM ICU.
Behrouz.M 1.06.2015 06:46:52
Также доступно с C ++ 11
martian 21.06.2018 18:06:37

boost :: iequals не совместим с utf-8 в случае строки. Вы можете использовать boost :: locale .

comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
  • Основной - игнорировать ударения и регистр символов, сравнивая только базовые буквы. Например, «фасад» и «фасад» - это одно и то же.
  • Вторичный - игнорировать регистр символов, но учитывать акценты. «фасад» и «фасад» различны, но «фасад» и «фасад» одинаковы.
  • Третичный - рассмотрим как корпус, так и ударения: «Фасад» и «Фасад» различны. Игнорировать знаки препинания
  • Четвертичный - рассмотреть все случаи, акценты и знаки препинания. Слова должны быть идентичны с точки зрения представления Unicode.
  • Идентично - как четвертичное, но сравните также и кодовые точки.
31
26.04.2012 08:51:29

На начало 2013 года проект ICU, поддерживаемый IBM, является довольно хорошим ответом на это.

http://site.icu-project.org/

ICU - это «полная, портативная библиотека Unicode, которая точно соответствует отраслевым стандартам». Для конкретной задачи сравнения строк объект Collation делает то, что вы хотите.

Проект Mozilla принял ICU для интернационализации в Firefox в середине 2012 года; Вы можете отслеживать технические обсуждения, включая вопросы систем сборки и размера файла данных, здесь:

2
1.04.2013 17:58:52

Для моего базового сравнения строк без учета регистра я предпочитаю не использовать внешнюю библиотеку, и при этом я не хочу иметь отдельный класс строк с чертами без учета регистра, которые несовместимы со всеми моими другими строками.

Итак, что я придумал, это:

bool icasecmp(const string& l, const string& r)
{
    return l.size() == r.size()
        && equal(l.cbegin(), l.cend(), r.cbegin(),
            [](string::value_type l1, string::value_type r1)
                { return toupper(l1) == toupper(r1); });
}

bool icasecmp(const wstring& l, const wstring& r)
{
    return l.size() == r.size()
        && equal(l.cbegin(), l.cend(), r.cbegin(),
            [](wstring::value_type l1, wstring::value_type r1)
                { return towupper(l1) == towupper(r1); });
}

Простая функция с одной перегрузкой для char и другой для whar_t. Не использует ничего нестандартного, поэтому должно работать на любой платформе.

Сравнение на равенство не учитывает такие проблемы, как кодирование переменной длины и нормализация Unicode, но basic_string не поддерживает этого, о чем я все равно знаю, и обычно это не проблема.

В тех случаях, когда требуется более сложное лексикографическое манипулирование текстом, вам просто нужно использовать стороннюю библиотеку, такую ​​как Boost, что и следовало ожидать.

9
28.06.2013 15:46:40
Вы могли бы сделать эту функцию, если бы сделали ее шаблоном и использовали basic_string <T> вместо отдельных версий string / wstring?
uliwitness 22.01.2014 13:31:28
Как бы один шаблон функции вызывал или toupper, или towupper, не прибегая к использованию специализации или макросов, перегрузка функции кажется более простой и более подходящей реализацией, чем любая из них.
Neutrino 28.06.2015 15:39:03

Просто используйте strcmp()для чувствительного к регистру и / strcmpi()или stricmp()для сравнения без учета регистра. Которые оба в заголовочном файле<string.h>

формат:

int strcmp(const char*,const char*);    //for case sensitive
int strcmpi(const char*,const char*);   //for case insensitive

Использование:

string a="apple",b="ApPlE",c="ball";
if(strcmpi(a.c_str(),b.c_str())==0)      //(if it is a match it will return 0)
    cout<<a<<" and "<<b<<" are the same"<<"\n";
if(strcmpi(a.c_str(),b.c_str()<0)
    cout<<a[0]<<" comes before ball "<<b[0]<<", so "<<a<<" comes before "<<b;

Вывод

яблоко и яблоко одинаковы

а до б, поэтому яблоко до мяча

4
26.07.2013 15:15:25
Downvote, потому что это вряд ли C ++ способ делать вещи.
Thomas Daugaard 30.07.2013 09:09:26
Это соглашение c ++ в моем университете, но я буду помнить его при публикации здесь
reubenjohn 13.08.2013 18:04:49
stricmp является расширением Microsoft AFAIK. В BSD вместо этого используется strcasecmp ().
uliwitness 22.01.2014 13:30:02
bool insensitive_c_compare(char A, char B){
  static char mid_c = ('Z' + 'a') / 2 + 'Z';
  static char up2lo = 'A' - 'a'; /// the offset between upper and lowers

  if ('a' >= A and A >= 'z' or 'A' >= A and 'Z' >= A)
      if ('a' >= B and B >= 'z' or 'A' >= B and 'Z' >= B)
      /// check that the character is infact a letter
      /// (trying to turn a 3 into an E would not be pretty!)
      {
        if (A > mid_c and B > mid_c or A < mid_c and B < mid_c)
        {
          return A == B;
        }
        else
        {
          if (A > mid_c)
            A = A - 'a' + 'A'; 
          if (B > mid_c)/// convert all uppercase letters to a lowercase ones
            B = B - 'a' + 'A';
          /// this could be changed to B = B + up2lo;
          return A == B;
        }
      }
}

это, вероятно, можно сделать гораздо более эффективным, но вот громоздкая версия со всеми ее битами.

не все такое портативное, но хорошо работает с тем, что есть на моем компьютере (не знаю, я из картинок, а не слов)

-1
5.03.2015 02:40:46
Это не поддержка Unicode, которая задается вопросом.
Behrouz.M 1.06.2015 13:06:11
Это не поддерживает неанглийские наборы символов.
Robert Andrzejuk 29.04.2018 15:02:55

Если вам приходится сравнивать исходную строку чаще с другими строками, одним из элегантных решений является использование регулярных выражений.

std::wstring first = L"Test";
std::wstring second = L"TEST";

std::wregex pattern(first, std::wregex::icase);
bool isEqual = std::regex_match(second, pattern);
0
30.09.2015 12:47:00
Пробовал это, но ошибка компиляции: error: conversion from 'const char [5]' to non-scalar type 'std::wstring {aka std::basic_string<wchar_t>}' requested
Deqing 15.05.2015 05:18:53
плохая идея. Это худшее решение.
Behrouz.M 1.06.2015 13:05:29
Это не очень хорошее решение, но даже если вы хотите его использовать, вам нужен L перед вашими самыми широкими константами, например, L "TEST"
celticminstrel 21.06.2015 02:27:20
Было бы хорошо, если бы кто-то мог объяснить, почему это худшее решение. Из-за проблем с производительностью? Создание регулярных выражений стоит дорого, но потом сравнение должно быть очень быстрым.
smibe 30.09.2015 12:49:07
он удобен в использовании и переносим, ​​главная проблема в том, что сначала он не может содержать символы, используемые регулярным выражением. Из-за этого его нельзя использовать в качестве общего сравнения строк. Это также будет медленнее, есть флаг, который заставит его работать так, как говорит smibe, но все еще не может использоваться как общая функция.
Ben 16.08.2016 21:37:49

Простой способ сравнить строки, которые различаются только строчными и прописными буквами, - сделать сравнение ascii. Все прописные и строчные буквы отличаются в 32-битной таблице ascii, используя эту информацию, мы имеем следующее ...

    for( int i = 0; i < string2.length(); i++)
    {
       if (string1[i] == string2[i] || int(string1[i]) == int(string2[j])+32 ||int(string1[i]) == int(string2[i])-32) 
    {
      count++;
      continue;
    }
    else 
    {
      break;
    }
    if(count == string2.length())
    {
      //then we have a match
    }
}
-3
12.05.2015 14:28:55
В соответствии с этим «++ j» будет найдено равным «KKJ», а «1234» будет найдено равным «QRST». Я сомневаюсь, что это то, что кто-то хочет.
celticminstrel 21.06.2015 02:24:31

Смотрите std::lexicographical_compare:

// lexicographical_compare example
#include <iostream>  // std::cout, std::boolalpha
#include <algorithm>  // std::lexicographical_compare
#include <cctype>  // std::tolower

// a case-insensitive comparison function:
bool mycomp (char c1, char c2) {
    return std::tolower(c1)<std::tolower(c2);
}

int main () {
    char foo[] = "Apple";
    char bar[] = "apartment";

    std::cout << std::boolalpha;

    std::cout << "Comparing foo and bar lexicographically (foo < bar):\n";

    std::cout << "Using default comparison (operator<): ";
    std::cout << std::lexicographical_compare(foo, foo + 5, bar, bar + 9);
    std::cout << '\n';

    std::cout << "Using mycomp as comparison object: ";
    std::cout << std::lexicographical_compare(foo, foo + 5, bar, bar + 9, mycomp);
    std::cout << '\n';

    return 0;
}

демонстрация

9
17.03.2018 08:17:18
Этот метод потенциально небезопасен и непереносим. std::tolowerработает, только если символ в кодировке ASCII. Нет такой гарантии для std::string- так что это может быть неопределенное поведение легко.
plasmacel 27.03.2018 14:27:54
@plasmacel Затем используйте функцию, которая работает с другими кодировками.
Brian Rodriguez 6.04.2018 15:05:24

Поздно до вечеринки, но вот вариант, который использует std::localeи, следовательно, правильно обрабатывает турецкий язык:

auto tolower = std::bind1st(
    std::mem_fun(
        &std::ctype<char>::tolower),
    &std::use_facet<std::ctype<char> >(
        std::locale()));

дает вам функтор, который использует активную локаль для преобразования символов в нижний регистр, который затем можно использовать с помощью std::transformдля генерации строчных букв:

std::string left = "fOo";
transform(left.begin(), left.end(), left.begin(), tolower);

Это также работает для wchar_tоснованных строк.

3
29.09.2015 00:25:21

Сделать это без использования Boost можно, получив указатель на строку C с c_str()помощью strcasecmp:

std::string str1 ="aBcD";
std::string str2 = "AbCd";;
if (strcasecmp(str1.c_str(), str2.c_str()) == 0)
{
    //case insensitive equal 
}
7
12.01.2016 09:19:06

Коротко и красиво. Никаких других зависимостей, кроме расширенных стандартных C lib.

strcasecmp(str1.c_str(), str2.c_str()) == 0

возвращает true, если str1и str2равны. strcasecmpможет не существовать, не может быть аналогами stricmp, strcmpiи т.д.

Пример кода:

#include <iostream>
#include <string>
#include <string.h> //For strcasecmp(). Also could be found in <mem.h>

using namespace std;

/// Simple wrapper
inline bool str_ignoreCase_cmp(std::string const& s1, std::string const& s2) {
    if(s1.length() != s2.length())
        return false;  // optimization since std::string holds length in variable.
    return strcasecmp(s1.c_str(), s2.c_str()) == 0;
}

/// Function object - comparator
struct StringCaseInsensetiveCompare {
    bool operator()(std::string const& s1, std::string const& s2) {
        if(s1.length() != s2.length())
            return false;  // optimization since std::string holds length in variable.
        return strcasecmp(s1.c_str(), s2.c_str()) == 0;
    }
    bool operator()(const char *s1, const char * s2){ 
        return strcasecmp(s1,s2)==0;
    }
};


/// Convert bool to string
inline char const* bool2str(bool b){ return b?"true":"false"; }

int main()
{
    cout<< bool2str(strcasecmp("asd","AsD")==0) <<endl;
    cout<< bool2str(strcasecmp(string{"aasd"}.c_str(),string{"AasD"}.c_str())==0) <<endl;
    StringCaseInsensetiveCompare cmp;
    cout<< bool2str(cmp("A","a")) <<endl;
    cout<< bool2str(cmp(string{"Aaaa"},string{"aaaA"})) <<endl;
    cout<< bool2str(str_ignoreCase_cmp(string{"Aaaa"},string{"aaaA"})) <<endl;
    return 0;
}

Вывод:

true
true
true
true
true
9
21.10.2016 19:05:20
Странно, что в C ++ std :: string нет метода сравнения с игнорированием.
kyb 30.09.2016 15:52:47
«strcasecmp не является частью стандарта» - Марк Рэнсом 1 декабря 14 года в 19:57
Liviu 21.10.2016 14:21:08
да, но большинство современных компиляторов имеют его или его другой аналог. stricmp, strcmpi, strcasecmpИ т.д. Спасибо. Сообщение отредактировано.
kyb 21.10.2016 19:01:17
TODO: используйте cout << boolalphaвместо my, bool2strпотому что он неявно конвертирует bool в chars для stream.
kyb 1.06.2017 12:15:07
Он находится в <strings.h> в библиотеках gcc.
Owl 7.08.2017 21:09:26
str1.size() == str2.size() && std::equal(str1.begin(), str1.end(), str2.begin(), [](auto a, auto b){return std::tolower(a)==std::tolower(b);})

Вы можете использовать приведенный выше код на C ++ 14, если вы не в состоянии использовать boost. Вы должны использовать std::towlowerдля широких символов.

12
21.10.2019 15:06:19
Я думаю, что вам нужно добавить a str1.size() == str2.size() &&вперед, чтобы не выходить за пределы, когда str2 является префиксом str1.
ɲeuroburɳ 1.08.2017 14:06:57

Похоже, что вышеупомянутые решения не используют метод сравнения и снова реализуют итоги, так что вот мое решение и надеюсь, что оно работает для вас (работает нормально).

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
string tolow(string a)
{
    for(unsigned int i=0;i<a.length();i++)
    {
        a[i]=tolower(a[i]);
    }
    return a;
}
int main()
{
    string str1,str2;
    cin>>str1>>str2;
    int temp=tolow(str1).compare(tolow(str2));
    if(temp>0)
        cout<<1;
    else if(temp==0)
        cout<<0;
    else
        cout<<-1;
}
2
26.08.2017 19:57:43

Простой способ сравнить две строки в c ++ (протестировано для Windows) - использовать _stricmp

// Case insensitive (could use equivalent _stricmp)  
result = _stricmp( string1, string2 );  

Если вы хотите использовать с std :: string, пример:

std::string s1 = string("Hello");
if ( _stricmp(s1.c_str(), "HELLO") == 0)
   std::cout << "The string are equals.";

Для получения дополнительной информации здесь: https://msdn.microsoft.com/it-it/library/e0z9k731.aspx

0
26.04.2018 21:42:10
В дополнение к этому ответу стоит прочитать stackoverflow.com/a/12414441/95309 , так как это a) функция C и b) предположительно не переносимая.
Claus Jørgensen 23.08.2018 12:18:13
что #include нам нужно, чтобы эта работа?
ekkis 22.06.2019 21:37:47
@ekkis, чтобы использовать _stricmp, вы должны включить <string.h>, как вы можете прочитать здесь: docs.microsoft.com/en-us/cpp/c-runtime-library/reference/…
DAme 1.07.2019 07:47:54

Если вы не хотите использовать библиотеку Boost, вот решение этой проблемы, использующее только стандартный C ++ заголовок io.

#include <iostream>

struct iequal
{
    bool operator()(int c1, int c2) const
    {
        // case insensitive comparison of two characters.
        return std::toupper(c1) == std::toupper(c2);
    }
};

bool iequals(const std::string& str1, const std::string& str2)
{
    // use std::equal() to compare range of characters using the functor above.
    return std::equal(str1.begin(), str1.end(), str2.begin(), iequal());
}

int main(void)
{
    std::string str_1 = "HELLO";
    std::string str_2 = "hello";

    if(iequals(str_1,str_2))
    {
        std::cout<<"String are equal"<<std::endl;   
    }

    else
    {
        std::cout<<"String are not equal"<<std::endl;
    }


    return 0;
}
1
5.10.2018 15:19:36
Я считаю, что std :: toupper находится в #include <cctype>, возможно, вам придется включить его.
David Ledger 2.12.2018 12:00:52
Если вы будете использовать глобальную версию наподобие this :: toupper, вам, возможно, не нужно будет включать <ctype>, потому что есть две версии c версия и версия c ++ с локалью, я думаю. Поэтому лучше использовать глобальную версию ":: toupper ()"
HaSeeB MiR 2.12.2018 13:52:15
это решение не работает, когда одна из строк пуста: "" - возвращает true в том случае, когда должно возвращаться false
ekkis 22.06.2019 21:35:26