Nano Hash - криптовалюты, майнинг, программирование

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

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

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

14.08.2008

  • @[Adam](#11679): Хотя этот вариант хорош с точки зрения удобства использования, он плох с точки зрения производительности, поскольку создает ненужные копии. Я мог бы что-то упустить, но я считаю, что лучший (не Unicode) способ - использовать std::stricmp. В противном случае прочитайте, что Херб должен сказать. 26.08.2008
  • В c обычно приходилось увеличивать всю строку, а затем сравнивать таким образом - или сверять собственное сравнение: P 22.05.2010
  • более поздний вопрос имеет более простой ответ: strcasecmp (по крайней мере, для компиляторов BSD и POSIX) сравнение строк c">stackoverflow.com/questions/9182912/ 06.11.2013
  • @Mσᶎ на этот вопрос также есть этот ответ, с важной оговоркой, что strcasecmp не является частью стандарта и отсутствует по крайней мере в одном общем компиляторе. 01.12.2014

Ответы:


1

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
}
24.11.2008
  • Является ли это UTF-8 дружественным? Думаю, нет. 30.10.2010
  • Нет, потому что UTF-8 позволяет кодировать идентичные строки разными двоичными кодами из-за акцентов, комбинаций, проблем с биди и т. д. 19.06.2011
  • @ vy32 Это абсолютно неправильно! Комбинации UTF-8 являются взаимоисключающими. Он всегда должен использовать кратчайшее возможное представление, если это не так, это неправильно сформированная последовательность UTF-8 или кодовая точка, с которой следует обращаться осторожно. 11.11.2011
  • @Wiz, вы игнорируете проблему нормализации строк Unicode. ñ может быть представлен как комбинация ˜, за которой следует n, или с символом ñ. Вам необходимо использовать нормализацию строки Unicode перед выполнением сравнения. Ознакомьтесь с техническим отчетом Unicode № 15, unicode.org/reports/tr15. 11.11.2011
  • @ vy32 (я никогда не следил за этим комментарием), но это еще не значит, что он не подходит для UTF-8. Сравнение должно быть нормализовано либо до полной декомпозиции, либо до полной компоновки, чтобы устранить такие проблемы эквивалентности. Тем не менее ничто не мешает функции iequals делать это. 23.01.2012
  • На самом деле я не уверен, подходит ли std::basic_string для кодирования переменной длины, такого как UTF8. Конечно, что-то сделает. Во многих случаях (в частности, в подмножестве ASCII) это будет работать правильно. Но я думаю, что std::basic_string (и, следовательно, все его использование) может предполагать кодировку фиксированной длины. Но, возможно, это аргумент шаблона Traits, который имеет дело с этим. 10.06.2012
  • Есть две отдельные проблемы, на которые следует обратить внимание: преобразование между UTF-32 и UTF-8 (фиксированное и детерминированное) и составление строки из кодовых точек UTF-32 (ни фиксированное, ни детерминированное). 21.03.2013
  • Для Unicode в общем случае работать не будет. ß и SS должны сравниваться равными, но алгоритмы Boost String не справляются с этим. 05.06.2013
  • @dalle Почему ß и SS должны сравниваться равными? В каких случаях? Большинство не хочет ß в Швейцарии, т.е. 10.12.2013
  • @wonkorealtime: поскольку ß, преобразованный в верхний регистр, является SS: fileformat.info/info /unicode/char/df/index.htm 30.05.2014
  • Небольшая проблема, #include ‹boost/algorithm/string/predicate.hpp› не работал, потому что не мог разрешить предикат, в отличие от версии без комментариев. Как правильно использовать минимальную версию зависимости заголовка? 30.06.2015
  • Также обратите внимание, что регистр также может зависеть от языка, даже в Unicode. т. е. в турецком языке заглавная буква U+0069 (строчная буква i) — это U+0130 (заглавная буква I с точкой), а не U+0049 (верхняя буква I). Их немного, но unicode.org/Public/UNIDATA/SpecialCasing.txt 12.06.2017
  • Определенно не поддерживает Unicode. Другим примером является греческая заглавная буква «Σ», которая преобразуется либо в строчную «σ», либо в «ς» в зависимости от положения слова. boost::iequals подчиняется std::locale(), который не может справиться с этими вещами. Все, что не относится к отделению интенсивной терапии, на данный момент лжет сквозь зубы. 07.02.2020
  • ОП не спрашивал, как это сделать с Boost. 24.11.2020

  • 2

    Воспользуйтесь преимуществами стандарта char_traits. Напомним, что std::string на самом деле является typedef для 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.

    22.05.2010
  • Насколько я знаю из собственных экспериментов, это делает ваш новый строковый тип несовместимым с std::string. 27.09.2012
  • Конечно делает - для своего же блага. Строка без учета регистра — это что-то другое: typedef std::basic_string<char, ci_char_traits<char> > istring, а не typedef std::basic_string<char, std::char_traits<char> > string. 09.10.2012
  • Я знаю, что это скопировано непосредственно из GotW29, и я бы предположил, что это широко цитируемое было правильным, но для меня (в Visual Studio 2005) функция поиска здесь не работает. Это приводит к тому, что basic_string::find переполняет буфер и завершается сбоем. Мне пришлось изменить return s; вернуть (n ›= 0 ? s : NULL);. 28.03.2013
  • Все, что вам нужно сделать... 19.04.2013
  • Метод compare() дважды вызывает toupper() для каждого символа. Вероятно, следует буферизовать результат toupper(), чтобы уменьшить нагрузку на ЦП. 13.02.2014
  • @Nathan, вероятно, использует компилятор, способный выполнять базовую CSE в коде ... 12.10.2014
  • Любая языковая конструкция, вызывающая такое безумие в этом тривиальном случае, должна и может быть оставлена ​​без сожаления. 14.11.2014
  • @ErikAronesty, а вы бы порекомендовали...? 15.11.2015
  • @DaveKennedy Я думаю, что Эрик советует отказаться от человеческих языков, поскольку это языковые конструкции, которые вызывают это безумие. :-) 21.03.2018
  • Я хотел бы отметить, что второй параметр find должен быть std::size_t, а не int. К сожалению, я не могу редактировать, потому что вопрос закрыт. Также возможно реализовать find и compare с точки зрения eq, lt и ne. 16.07.2018
  • Хотя это делает строку без учета регистра несовместимой с std::string, преобразование между ними тривиально с помощью конструктора диапазона. 11.10.2019
  • Дополнительное уточнение: на первый взгляд этот метод кажется несовместимым с std::unordered_map. (Или, по крайней мере, реализация строкового хэша в стандартной библиотеке MSVC, по-видимому, не использует признаки char для чего-либо.) Таким образом, при использовании этого с std::unordered_map, вероятно, также потребуется специализация std::hash. 19.03.2020
  • std::toupper следует не вызывать char напрямую, а static_cast для unsigned char нужно. 26.09.2020
  • С C++17 мы можем использовать string_view для использования ci_char_traits. 02.10.2020

  • 3

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

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

    Этого должно быть достаточно. Он должен быть достаточно эффективным. Однако не обрабатывает юникод или что-то в этом роде.

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

    Обновление: бонусная версия С++ 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);
                          });
    }
    
    07.11.2010
  • На самом деле, библиотека строк повышения является библиотекой только для заголовков, поэтому нет необходимости ссылаться на что-либо. Кроме того, вы можете использовать утилиту 'bcp' от boost, чтобы скопировать только заголовки строк в ваше исходное дерево, так что вам не потребуется полная библиотека boost. 10.03.2011
  • Ах, я не знал о bcp, это выглядит действительно полезно. Спасибо за информацию! 13.03.2011
  • Полезно знать простую и независимую версию. 17.05.2014
  • @Anna Текстовая библиотека boost должна быть создана и связана. Он использует IBM ICU. 01.06.2015
  • Также доступно с C++11 21.06.2018
  • std::equal недоступен в C++11. 22.06.2018
  • std::tolower следует не вызывать char напрямую, а static_cast для unsigned char нужно. 26.09.2020
  • @Evg Это довольно безумно. К счастью, это похоже на одно из тех технически неопределенных действий, но все равно все делают разумные вещи, так что де-факто это, вероятно, нормально. Я проверил GCC, Clang и ICC с помощью -O3, и все они работают нормально. Я предполагаю, что если бы вы сделали компилятор, который этого не сделал, он не смог бы скомпилировать много существующего кода. 02.03.2021

  • 4

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

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

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

    14.08.2008

    5

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

    14.08.2008
  • Вы можете изменить эту ссылку ICU на site.icu-project.org. 13.12.2017

  • 6

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

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

    7

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

    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(static_cast<unsigned char>(*c1)) != tolower(static_cast<unsigned char>(*c2))) {
                return false;
            }
        }
        return true;
    }
    
    26.08.2008
  • std::tolower следует не вызывать char напрямую, а static_cast для unsigned char нужно. 26.09.2020
  • @Evg, так что if (tolower(static_cast<unsigned char>(*c1)) != tolower(static_cast<unsigned char>(*c2)) подойдет? 27.09.2020
  • Да, это должен быть правильный путь. 27.09.2020
  • @Evg, исправлено. Спасибо. 27.09.2020

  • 8

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

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

    02.12.2008
  • Поскольку определение длины строки состоит из перебора каждого символа в строке и сравнения его с 0, действительно ли есть большая разница между этим и простым сравнением строк сразу? Я предполагаю, что вы получаете лучшую локальность памяти в случае, когда обе строки не совпадают, но, вероятно, почти в 2 раза больше времени выполнения в случае совпадения. 22.01.2014
  • C++11 указывает, что сложность std::string::length должна быть постоянной: cplusplus.com/reference/string/string/length 05.02.2014
  • Это забавный маленький факт, но здесь он не имеет большого значения. И strcasecmp(), и stricmp() принимают необработанные строки C, поэтому std::string не используется. 05.02.2014
  • Эти методы вернут -1, если вы сравните a с ab. Длины разные, но а предшествует ab. Таким образом, простое сравнение длин невозможно, если вызывающая сторона заботится об упорядочении. 13.02.2014

  • 9

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

    Вот способ сделать это, хотя он преобразует строки и не поддерживает 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 (International Components for Unicode).

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

    14.08.2008
  • не могли бы вы рассказать, что означает ::tolower, почему вы можете использовать tolower вместо tolower() и что такое '::' раньше? Благодарность 11.03.2011
  • Это не очень эффективное решение — вы делаете копии обеих строк и трансформируете их все, даже если первый символ отличается. 13.03.2011
  • Если вы все равно собираетесь делать копию, почему бы не передать по значению, а не по ссылке? 21.06.2015
  • Я думаю, что это простой совет без наддува. :) 08.11.2016
  • вопрос явно просит не transform всю строку перед сравнением 06.06.2019
  • std::tolower следует не вызывать char напрямую, а static_cast для unsigned char нужно. 26.09.2020

  • 10

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

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

    14.08.2008
  • По иронии судьбы, коды широких символов Microsoft НЕ являются чистыми для юникода, потому что они не обрабатывают нормализацию юникода. 19.06.2011

  • 11

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

    14.08.2008
  • Верно, хотя перезапись буфера значительно менее опасна, чем перезапись буфера. 17.11.2008
  • stricmp() и strnicmp() не являются частью стандарта POSIX :-( Однако вы можете найти strcasecmp(), strcasecmp_l(), strncasecmp() и strncasecmp_l() в заголовке POSIX strings.h :-) см. opengroup.org 11.04.2013
  • @AdamRosenfield «хуже» зависит от контекста. В безопасности иногда весь смысл перезаписи состоит в том, чтобы перечитать. 21.03.2015

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

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

  • 13

    См. 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;
    }
    

    Демо

    16.09.2015
  • Этот метод потенциально небезопасен и непереносим. std::tolower работает, только если символ закодирован в ASCII. Для std::string такой гарантии нет, поэтому это может быть легко неопределенное поведение. 27.03.2018
  • @plasmacel Тогда используйте функцию, которая работает с другими кодировками. 06.04.2018

  • 14

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

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

    22.05.2010
  • В std::string нет встроенного способа? 22.05.2010
  • Нет, нет. 22.05.2010
  • ... зачем беспокоиться, если это уже сделано? - что, если вы не используете Boost? У ОП не было тега с вопросом. 08.02.2016

  • 15

    Коротко и красиво. Никаких других зависимостей, кроме extended std 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
    
    30.09.2016
  • странно, что С++ std::string не имеет метода сравнения с игнорированием регистра. 30.09.2016
  • strcasecmp не является частью стандарта — Марк Рэнсом 21.10.2016
  • да, но в большинстве современных компиляторов он или его аналог с другим названием есть. stricmp, strcmpi, strcasecmp и т. д. Спасибо. сообщение отредактировано. 21.10.2016
  • TODO: используйте cout << boolalpha, а не мой bool2str, потому что он неявно преобразует логическое значение в символы для потока. 01.06.2017
  • Он находится в ‹strings.h› в библиотеках gcc. 08.08.2017

  • 16

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

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

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

    26.06.2013
  • Вероятно, вы могли бы сделать эту функцию, если бы сделали ее шаблоном и использовали basic_string‹T› вместо отдельных версий string/wstring? 22.01.2014
  • Как бы шаблон с одной функцией вызывал либо toupper, либо towupper, не прибегая к использованию специализации или макросов, перегрузка функции кажется более простой и подходящей реализацией, чем любая из них. 28.06.2015

  • 17

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

    18

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

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

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

    14.08.2008

    19

    Я написал нечувствительную к регистру версию 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;
    
    17.11.2008
  • Это работает для обычных символов, но не будет работать для всего Unicode, так как заглавные буквы не обязательно двунаправленные (есть хороший пример в греческом языке с сигмой, который я сейчас не могу вспомнить; что-то вроде двух строчных и одного верхнего регистра , и вы не можете получить правильное сравнение в любом случае) 25.11.2008
  • Это действительно неправильный подход. Чувствительность к регистру не должна быть свойством самих строк. Что происходит, когда один и тот же строковый объект требует сравнения как с учетом регистра, так и без учета регистра? 25.11.2008
  • Если чувствительность к регистру не подходит для использования в строке, то и функция find() вообще не подходит. Что для вас может быть правдой, и это нормально. IMO, самое лучшее в C++ то, что он не навязывает программисту определенную парадигму. Это то, что вы хотите/нужно, чтобы это было. 25.11.2008
  • На самом деле, я думаю, что большинство гуру C++ (например, в комитете по стандартам) согласны с тем, что было ошибкой помещать find() в std::basic_string‹› вместе со многими другими вещами, которые с таким же успехом можно было бы поместить в бесплатные функции. Кроме того, есть некоторые проблемы с вводом его в тип. 25.11.2008
  • Как указывали другие, в этом решении есть две основные проблемы (по иронии судьбы, одна - интерфейс, а другая - реализация ;-)). 25.11.2008
  • … но поскольку Херб Саттер совершил ту же ошибку, и я, по-видимому, даже дал ссылку на его статью (я этого не помню!), я не могу жаловаться. 25.11.2008

  • 20

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

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

    14.08.2008

    21

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

    Вывод

    apple и ApplE — это одно и то же

    а предшествует b, поэтому яблоко предшествует мячу

    25.07.2013
  • Понизьте голос, потому что это вряд ли способ ведения дел на С++. 30.07.2013
  • Это соглашение С++ в моем университете, но я буду помнить об этом, когда публикую здесь 13.08.2013
  • stricmp — это расширение Microsoft, насколько мне известно. BSD, кажется, имеет strcasecmp() вместо этого. 22.01.2014

  • 22

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

    29.09.2015

    23

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

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

    25.11.2008

    24

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

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

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

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

    01.04.2013

    25

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

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

    26

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

    18.04.2018
  • Стоит прочитать stackoverflow.com/a/12414441/95309 в дополнение к этому ответу, так как это а) функция C, и б) предположительно не портативный. 23.08.2018
  • какой #include нам нужен, чтобы это заработало? 23.06.2019
  • @ekkis, чтобы использовать _stricmp, вы должны включить ‹string.h›, как вы можете прочитать здесь: docs.microsoft.com/en-us/cpp/c-runtime-library/reference/ 01.07.2019

  • 27

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

    #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;
    }
    
    26.04.2018
  • Я считаю, что std::toupper находится в #include ‹cctype›, вам может понадобиться включить его. 02.12.2018
  • Если вы будете использовать глобальную версию, подобную этой ::toupper, вам может не понадобиться включать ‹ctype›, потому что, я думаю, есть две версии: версия c и версия C++ с локалью. Так что лучше использовать глобальную версию ::toupper() 02.12.2018
  • это решение терпит неудачу, когда одна из строк пуста: -- оно возвращает true в том случае, когда должно возвращать false 23.06.2019

  • 28

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

    std::wstring first = L"Test";
    std::wstring second = L"TEST";
    
    std::wregex pattern(first, std::wregex::icase);
    bool isEqual = std::regex_match(second, pattern);
    
    06.03.2015
  • Пробовал это, но ошибка компиляции: error: conversion from 'const char [5]' to non-scalar type 'std::wstring {aka std::basic_string<wchar_t>}' requested 15.05.2015
  • плохая идея. Это худшее решение. 01.06.2015
  • Это не очень хорошее решение, но даже если вы хотите его использовать, вам нужна буква L перед вашими широкими константами, например LTEST. 21.06.2015
  • Было бы неплохо, если бы кто-нибудь мог объяснить, почему это худшее решение. Из-за проблем с производительностью? Создание регулярного выражения стоит дорого, но потом сравнение должно быть очень быстрым. 30.09.2015
  • его можно использовать и переносить, основная проблема заключается в том, что first не может содержать никаких символов, которые использует регулярное выражение. Из-за этого его нельзя использовать в качестве общего сравнения строк. Это также будет медленнее, есть флаг, чтобы заставить его работать так, как говорит smibe, но по-прежнему нельзя использовать в качестве общей функции. 17.08.2016

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

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

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

    05.03.2015
  • Это не поддержка Unicode, о чем и был задан вопрос. 01.06.2015
  • Это не поддерживает неанглийские наборы символов. 29.04.2018

  • 30

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

        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
        }
    }
    
    12.05.2015
  • В соответствии с этим ++j будет найдено равным KKJ, а 1234 будет найдено равным QRST. Сомневаюсь, что это кому-то нужно. 21.06.2015
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..