Порівняння рядкових рядків у регістрі C ​​++ [закрито]


373

Який найкращий спосіб порівняння рядків, нечутливих до регістру, у C ++, не перетворюючи рядок у всі великі регістри чи всі маленькі регістри?

Будь ласка, вкажіть, чи методи підходять Unicode і наскільки вони портативні.


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

У c, зазвичай, людину змушували перетискати цілу струну, а потім порівнювати таким чином - або згорнути власне порівняння: P
Michael Dorgan

більш пізній питання має просту відповідь: strcasecmp (принаймні для укладачів BSD і POSIX) stackoverflow.com/questions/9182912 / ...
Мос

@ Mσᶎ на це питання також є відповідь, важливий застереження, яке strcasecmpне є частиною стандарту і не містить хоча б одного загального компілятора.
Марк Викуп 11

Відповіді:


317

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? Я думаю, НЕ.
vladr

18
Ні, тому що UTF-8 дозволяє кодувати однакові рядки різними двійковими кодами, через акценти, комбайни, випуски bidi тощо
vy32

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

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

12
@wonkorealtime: тому що "ß" перетворюється в верхній регістр є "СС": 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
"Все, що вам потрібно зробити ..."
Tim MB

3
@Nathan, ймовірно, використовує компілятор, який здатний виконувати базовий 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;
}

Оновлення: Bonus 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
Насправді бібліотека рядків підсилювальних рядків - це лише бібліотека із заголовком, тому немає необхідності посилатися ні на що. Крім того, ви можете скористатися утилітою 'bcp' boost, щоб скопіювати лише заголовки рядків до вашого вихідного дерева, тому вам не потрібно вимагати повної бібліотеки підвищення.
Гретхен

Ах, я не знав про bcp, це виглядає дуже корисно. Дякую за інформацію!
Тиммммм

9
Добре знати просту версію, яка не залежить від посилення.
Декінг

2
@Anna Текстову бібліотеку підсилення потрібно створити та зв’язати. Тут використовується IBM ICU.
Бероуз.М

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

58

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

Якщо цього не відбувається, велика кількість реалізацій історичної бібліотеки С має функції 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

Першою моєю думкою для версії, що не містить коду, було зробити щось подібне:


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 або stricmpв Windows.

Одне, що до цього часу не згадувалося, це те, що якщо ви використовуєте рядки stl з цими методами, корисно спочатку порівняти довжину двох рядків, оскільки ця інформація вже доступна вам у класі рядків. Це може запобігти порівнянню дорогого рядка, якщо два рядки, які ви порівнюєте, не мають однакової довжини в першу чергу.


Оскільки визначення довжини рядка складається з повторення кожного символу в рядку та порівняння його з 0, чи справді існує велика різниця між цим і просто порівняння рядків відразу? Я думаю, ви покращуєте локальність пам’яті у випадку, коли обидва рядки не збігаються, але, ймовірно, майже в 2 рази виконуються у разі збігу.
uliwitness

3
C ++ 11 вказує, що складність std :: string :: length має бути постійною: cplusplus.com/reference/string/string/length
bradtgmurray

1
Це дуже цікавий факт, але мало стосунків тут. і strcasecmp (), і stricmp () беруть некольоровані C рядки, тому немає std :: string.
uliwitness

3
Ці методи повернуть -1, якщо порівнювати "а" проти "аб". Довжини різні, але "a" відбувається перед "ab". Отже, просто порівнювати довжини не представляється можливим, якщо абонент дбає про замовлення.
Натан


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, якщо ви не можете використовувати прискорення. Ви повинні використовувати std::towlowerдля широких символів.


4
Я думаю, що вам потрібно додати a str1.size() == str2.size() &&на передню частину, щоб не виходити за межі, коли str2 є приставкою str1.
ɲeuroburɳ

11

Бібліотека Boost.String має безліч алгоритмів для порівняння, що не враховує регістри, тощо.

Ви можете реалізувати своє, але навіщо турбуватися, коли це вже зроблено?


1
Не існує вбудованого способу за допомогою std :: string?
WilliamKF

6
Ні, немає.
Дін Хардінг

3
"... навіщо турбуватися, коли це вже зроблено?" - що робити, якщо ви не використовуєте Boost? В ОП не було мітки із запитанням.
jww

11

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


6
Щоправда, хоча overREAD буфера значно менш небезпечний, ніж overWRITEing буфера.
Адам Розенфілд

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

Демо


Цей метод потенційно небезпечний і не переноситься. std::tolowerпрацює лише в тому випадку, якщо символ кодований ASCII. Немає такої гарантії std::string- тому її можна легко визначити.
плазмацель

@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
Як би єдиний шаблон функції викликав або таппер, або букпер, не вдаючись до використання спеціалізації чи макросів, перевантаження функції здається більш простою та більш доцільною реалізацією, ніж будь-яка.
Нейтрино

9

Короткий і приємний. Ніяких інших залежностей, ніж розширений std C lib.

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

повертає істину, якщо 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 не є частиною стандарту" - Mark Ransom 1 грудня '14 о 19:57
Liviu

так, але більшість сучасних компіляторів мають його або його інший названий аналог. stricmp, strcmpi, strcasecmpІ т.д. Спасибі. повідомлення відредаговане.
киб

TODO: використовуйте, cout << boolalphaа не мій, bool2strоскільки це неявно перетворює bool в символи для потоку.
киб

Це в <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 :: рядок, який не відрізняється від регістру при порівнянні, пошуку тощо, використовуючи вбудовані функції 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;

1
Це працює для звичайних знаків, але не буде працювати для всіх Unicode, оскільки капіталізація не обов'язково є двонаправленою (у грецькій мові є хороший приклад, що включає сигму, яку я зараз не пам'ятаю; щось подібне має два нижнього та один верхній регістр , і ви не можете отримати належного порівняння в будь-якому випадку)
coppro

1
Це дійсно неправильний шлях для цього. Чутливість до регістру не повинна бути властивістю самих струн. Що відбувається, коли один і той же рядковий об'єкт потребує порівняння з урахуванням регістру та чутливості до регістру?
Ferruccio

Якщо чутливість до регістру не підходить бути "частиною" рядка, то функція find () взагалі не є. Що, для вас, може бути правдою, і це добре. Найбільше, що стосується ІМО, щодо C ++ - це те, що воно не змушує програмувати певну парадигму. Це те, що ти хочеш / потребуєш.
Джон Дайблінг

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

Як зазначали інші, в цьому рішенні не так, що це не так (як не дивно, одна інтерфейс, а інша - реалізація ;-)).
Конрад Рудольф

4

Я мав хороший досвід використання міжнародних компонентів для бібліотек 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;

Вихідні дані

apple і ApPlE - те саме

a приходить перед 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, то тут для цього є рішення, використовуючи лише стандартний заголовок io + 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;
}

Я вважаю, що std :: toupper є в #include <cctype>, можливо, вам потрібно буде включити його.
Девід Леджер

Якщо ви будете використовувати глобальну версію на зразок цієї :: toupper, то, можливо, вам не потрібно буде включати <ctype>, оскільки існує дві версії c версія та версія c ++ з локальним словом. Тож краще скористатися глобальною версією ":: toupper ()"
HaSeeB MiR

це рішення виходить з ладу, коли одна з рядків порожня: "" - повертається істиною в тому випадку, коли він повинен повернути помилкове
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
Декінг

погана ідея. Це найгірше рішення.
Беруз.М.

Це не гарне рішення, але навіть якщо ви хотіли ним скористатися, вам знадобиться L перед своїми широкими константами, наприклад L "TEST"
celticminstrel

Було б добре, якби хтось міг пояснити, чому це найгірше рішення. Через проблеми з роботою? Створення регулярного вираження є дорогим, але після цього порівняння має бути справді швидким.
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 , оскільки це а) функція С та б) нібито не є портативною.
Клаус Йоргенсен

що нам # потрібно, щоб зробити цю роботу?
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.