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


373

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

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


@ [Адам] (# 11679): Хотя этот вариант хорош с точки зрения удобства использования, он плох с точки зрения производительности, поскольку создает ненужные копии. Я мог бы что-то упустить, но я считаю, что лучший (не Unicode) способ это использовать std::stricmp. В противном случае, прочитайте, что Херб должен сказать .
Конрад Рудольф

В случае c обычно нужно перебирать всю строку, а затем сравнивать таким образом - или бросать собственное сравнение: P
Майкл Дорган,

более поздний вопрос имеет более простой ответ: strcasecmp (по крайней мере для компиляторов BSD & POSIX) stackoverflow.com/questions/9182912/…
Móż

@ Mσᶎ на этот вопрос также есть ответ, с важным предупреждением, strcasecmpкоторое не является частью стандарта и отсутствует по крайней мере в одном общем компиляторе.
Марк Рэнсом

Ответы:


318

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
}

14
Это UTF-8 дружественный? Думаю, нет.
Влад

18
Нет, потому что UTF-8 позволяет кодировать идентичные строки с разными двоичными кодами из-за акцентов, сочетаний, проблем с биди и т. Д.
vy32

10
@ vy32 Это абсолютно неверно! Комбинации UTF-8 являются взаимоисключающими. Он всегда должен использовать кратчайшее возможное представление, если это не так, то это неправильно сформированная последовательность UTF-8 или кодовая точка, к которой следует обращаться с осторожностью.
Wiz

48
@Wiz, вы игнорируете проблему нормализации строки Unicode. ñ может быть представлен как комбинация ˜, за которой следует n или символ ñ. Вам необходимо использовать нормализацию строки Unicode перед выполнением сравнения. Пожалуйста , ознакомьтесь с Unicode Технический отчет № 15, unicode.org/reports/tr15
vy32

12
@wonkorealtime: потому что «ß», преобразованный в верхний регистр, это «SS»: fileformat.info/info/unicode/char/df/index.htm
Duck

118

Воспользуйтесь преимуществом стандарта 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 .


10
Насколько я знаю из моего собственного эксперимента, это делает ваш новый строковый тип несовместимым с std :: string.
Зан Рысь,

8
Конечно, это - для его же блага. Строка без учета регистра есть нечто другое: typedef std::basic_string<char, ci_char_traits<char> > istringне typedef std::basic_string<char, std::char_traits<char> > string.
Андреас Шпиндлер

232
«Все, что вам нужно сделать ...»
Тим М.Б.

3
@ Натан, вероятно, использует компилятор, который способен выполнять базовую CSE на коде ...
Парамагнитный круассан

17
Любая языковая конструкция, которая вызывает такое безумие в этом тривиальном случае, должна быть и может быть оставлена ​​без сожалений.
Эрик Аронести

86

Проблема с бустом в том, что вы должны связываться с бустом и зависеть от него. Не легко в некоторых случаях (например, 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);
                      });
}

27
На самом деле, библиотека String String - это библиотека только для заголовков, поэтому нет необходимости ссылаться на что-либо. Кроме того, вы можете использовать утилиту boost 'bcp', чтобы скопировать только заголовки строк в ваше дерево исходных текстов, поэтому вам не требуется полная библиотека boost.
Гретхен

Ах, я не знал о BCP, это выглядит действительно полезным. Спасибо за информацию!
Тимммм

9
Полезно знать простую версию без зависимостей.
Deqing

2
@Anna Текстовая библиотека наддува должна быть построена и иметь ссылку. Он использует IBM ICU.
Behrouz.M

Также доступно с C ++ 11
марсианский

58

Если вы работаете в системе 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.


53

Вы говорите о немом сравнении без учета регистра или полном нормализованном сравнении 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


2
Вы можете отредактировать эту ссылку ICU на site.icu-project.org
DevSolar

31

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

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

30

Моей первой мыслью для не-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;
}

20

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

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


Поскольку определение длины строки состоит из итерации по каждому символу в строке и сравнения ее с 0, действительно ли так много различий между этим и просто сравнение строк сразу? Я предполагаю, что вы получаете лучшую локальность памяти в случае, когда обе строки не совпадают, но, вероятно, почти в 2 раза больше времени выполнения в случае совпадения.
Uliwitness

3
В C ++ 11 указано, что сложность std :: string :: length должна быть постоянной: cplusplus.com/reference/string/string/length
bradtgmurray

1
Это забавный маленький факт, но здесь мало что значит. strcasecmp () и stricmp () принимают недекорированные строки C, поэтому в них не используется std :: string.
'39

3
Эти методы вернут -1, если вы сравните «a» с «ab». Длина различна, но «а» предшествует «а». Таким образом, простое сравнение длин не представляется возможным, если вызывающая сторона заботится о заказе.
Натан

14

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

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


7
По иронии судьбы, «широкие символьные коды» Microsoft НЕ чисты в Юникоде, потому что они не обрабатывают нормализацию Юникода.
vy32

13

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

Вот способ сделать это, хотя он преобразует строки и не поддерживает 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 предоставляет довольно хорошую утилиту для такого рода сравнения.


Подскажите, пожалуйста, что означает :: tolower, почему вы можете использовать tolower вместо tolower () и что такое «::» раньше? спасибо
VextoR

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

2
Если вы все равно собираетесь сделать копию, почему бы не передать по значению, а не по ссылке?
celticminstrel

Я думаю, что это простой совет без повышения. :)
cmcromance

1
вопрос задает явно не transformвсю строку перед сравнением
Сандбург

12
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для широких символов.


4
Я думаю, вам нужно добавить a str1.size() == str2.size() &&вперед, чтобы не выходить за пределы, когда str2 является префиксом str1.
uroeuroburɳ

11

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

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


1
Нет способа, встроенного в std :: string?
WilliamKF

6
Нет, нет
Дин Хардинг

3
"... зачем, когда это уже сделано?" - что делать, если вы не используете Boost? У ОП не было тега с вопросом.
jww

11

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


6
Правда, хотя перезапись буфера значительно менее опасна, чем перезапись буфера.
Адам Розенфилд

4
stricmp()и strnicmp()не являются частью стандарта POSIX :-( Однако вы можете найти strcasecmp(), strcasecmp_l(), strncasecmp()и strncasecmp_l()в заголовке POSIX strings.h:-) см opengroup.org
olibre

2
@AdamRosenfield «хуже» зависит от контекста. В области безопасности иногда весь смысл перезаписи заключается в том, чтобы перезаписать.
Кармаказе

10

Смотрите 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;
}

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


1
Этот метод потенциально небезопасен и непереносим. std::tolowerработает, только если символ в кодировке ASCII. Нет такой гарантии для std::string- так что это может быть неопределенное поведение легко.
plasmacel

@plasmacel Затем используйте функцию, которая работает с другими кодировками.
Брайан Родригес

9

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

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

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, что и следовало ожидать.


2
Вы могли бы сделать эту функцию, если бы сделали ее шаблоном и использовали basic_string <T> вместо отдельных версий string / wstring?
Uliwitness

2
Как бы один шаблон функции вызывал toupper или towupper без использования специализации или макросов, перегрузка функции кажется более простой и более подходящей реализацией, чем любая из них.
Нейтрино

9

Коротко и красиво. Никаких других зависимостей, кроме расширенных стандартных 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

6
Странно, что в C ++ std :: string нет метода сравнения игнорируемых случаев.
kyb

1
«strcasecmp не является частью стандарта» - Марк Рэнсом 1 декабря 14 года в 19:57
Ливиу

да, но большинство современных компиляторов имеют его или его другой аналог. stricmp, strcmpi, strcasecmpИ т.д. Спасибо. Сообщение отредактировано.
kyb

TODO: используйте cout << boolalphaвместо my, bool2strпотому что он неявно конвертирует bool в chars для stream.
KYB

Он находится в <strings.h> в библиотеках gcc.
Сова

7

Сделать это без использования 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 
}

6

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

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

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


6

Я написал версию 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;

2
Это работает для обычных символов, но не будет работать для всего Unicode, поскольку капитализация не обязательно является двунаправленной (есть хороший пример греческого языка с сигмой, который я не могу вспомнить сейчас; что-то вроде этого имеет два нижних и один верхний регистр , и вы не можете получить надлежащее сравнение в любом случае)
Коппро

1
Это действительно неправильный путь. Чувствительность к регистру не должна быть свойством самих строк. Что происходит, когда один и тот же строковый объект нуждается в сравнении с учетом регистра и без учета регистра?
Ферруччо

Если чувствительность к регистру не подходит, чтобы быть «частью» строки, тогда ни одна из них не является функцией find (). Что для вас может быть правдой, и это нормально. ИМО самое главное в C ++ - это то, что он не навязывает определенную парадигму программисту. Это то, что вы хотите / нужно, чтобы это было.
Джон Диблинг

На самом деле, я думаю, что большинство C ++ - гуру (например, те, что входят в комитет по стандартам) согласны с тем, что было ошибкой помещать find () в std :: basic_string <> вместе с целым рядом других вещей, которые с таким же успехом можно было бы поместить в свободные функции. Кроме того, есть некоторые проблемы с помещением этого типа.
Андреас Магнуссон

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

4

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

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


4

Просто используйте 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;

Вывод

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

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


2
Downvote, потому что это вряд ли C ++ способ делать вещи.
Томас Дауго

Это соглашение c ++ в моем университете, но я буду помнить его при публикации здесь
reubenjohn

4
stricmp является расширением Microsoft AFAIK. В BSD вместо этого используется strcasecmp ().
Uliwitness

3

Поздно до вечеринки, но вот вариант, который использует 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основанных строк.


2

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

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


2

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

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

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

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


2

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

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

1

Если вы не хотите использовать библиотеку 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;
}

Я считаю, что std :: toupper находится в #include <cctype>, возможно, вам придется включить его.
Дэвид Леджер

Если вы будете использовать глобальную версию наподобие this :: toupper, то вам, возможно, не потребуется включать <ctype>, потому что есть две версии c версия и версия c ++ с локалью, я думаю. Поэтому лучше использовать глобальную версию ":: toupper ()"
HaSeeB MiR

это решение завершается ошибкой, когда одна из строк пуста: "" - возвращает true в том случае, если оно должно возвращать false
ekkis

0

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

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

std::wregex pattern(first, std::wregex::icase);
bool isEqual = std::regex_match(second, pattern);

Пробовал это, но ошибка компиляции: error: conversion from 'const char [5]' to non-scalar type 'std::wstring {aka std::basic_string<wchar_t>}' requested
Deqing

плохая идея. Это худшее решение.
Behrouz.M

Это не очень хорошее решение, но даже если вы хотите его использовать, вам нужен L перед вашими самыми широкими константами, например, L "TEST"
celticminstrel

Было бы хорошо, если бы кто-то мог объяснить, почему это худшее решение. Из-за проблем с производительностью? Создание регулярных выражений стоит дорого, но после этого сравнение должно быть очень быстрым.
smibe

он удобен в использовании и переносим, ​​основная проблема в том, что сначала он не может содержать символы, используемые регулярным выражением. Из-за этого его нельзя использовать в качестве общего сравнения строк. Это также будет медленнее, есть флаг, который заставляет его работать так, как говорит smibe, но все еще не может использоваться как общая функция.
Бен

0

Простой способ сравнить две строки в 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


В дополнение к этому ответу стоит прочитать stackoverflow.com/a/12414441/95309 , так как это a) функция C и b) предположительно не переносимая.
Клаус Йоргенсен

что #include нам нужно, чтобы эта работа?
ekkis

1
@ekkis, чтобы использовать _stricmp, вы должны включить <string.h>, как вы можете прочитать здесь: docs.microsoft.com/en-us/cpp/c-runtime-library/reference/…
DAme

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

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

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


Это не поддержка Unicode, которая задается вопросом.
Behrouz.M

Это не поддерживает неанглийские наборы символов.
Роберт Анджеюк

-3

Простой способ сравнения строк, которые различаются только строчными и прописными буквами, состоит в сравнении 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
В соответствии с этим «++ j» будет найдено равным «KKJ», а «1234» будет найдено равным «QRST». Я сомневаюсь, что это то, что кто-то хочет.
celticminstrel
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.