Нечувствительный к регистру станд.:: string.find ()

Я использую std::string find() метод, чтобы протестировать, если строка является подстрокой другого. Теперь мне нужна нечувствительная к регистру версия того же самого. Для сравнения строк я могу всегда обращаться к stricmp() но, кажется, нет a stristr().

Я нашел различные ответы, и большинство предлагает использовать Boost который не является опцией в моем случае. Кроме того, я должен поддерживать std::wstring/wchar_t. Какие-либо идеи?

58
задан wpfwannabe 30 June 2010 в 18:28
поделиться

4 ответа

Вы можете использовать std::search с пользовательским предикатом.

#include <locale>
#include <iostream>
#include <algorithm>
using namespace std;

// templated version of my_equal so it could work with both char and wchar_t
template<typename charT>
struct my_equal {
    my_equal( const std::locale& loc ) : loc_(loc) {}
    bool operator()(charT ch1, charT ch2) {
        return std::toupper(ch1, loc_) == std::toupper(ch2, loc_);
    }
private:
    const std::locale& loc_;
};

// find substring (case insensitive)
template<typename T>
int ci_find_substr( const T& str1, const T& str2, const std::locale& loc = std::locale() )
{
    typename T::const_iterator it = std::search( str1.begin(), str1.end(), 
        str2.begin(), str2.end(), my_equal<typename T::value_type>(loc) );
    if ( it != str1.end() ) return it - str1.begin();
    else return -1; // not found
}

int main(int arc, char *argv[]) 
{
    // string test
    std::string str1 = "FIRST HELLO";
    std::string str2 = "hello";
    int f1 = ci_find_substr( str1, str2 );

    // wstring test
    std::wstring wstr1 = L"ОПЯТЬ ПРИВЕТ";
    std::wstring wstr2 = L"привет";
    int f2 = ci_find_substr( wstr1, wstr2 );

    return 0;
}
72
ответ дан 24 November 2019 в 18:42
поделиться

Если вы хотите «реальное» сравнение согласно правилам Unicode и локали, используйте класс ICU Collator .

2
ответ дан 24 November 2019 в 18:42
поделиться

Почему бы просто не преобразовать обе строки в нижний регистр перед вызовом find () ?

tolower

Примечание:

13
ответ дан 24 November 2019 в 18:42
поделиться

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

Тем не менее, сделать это достаточно просто: просто преобразовать обе строки в верхний регистр (или обе в нижний регистр - в этом примере я выбрал верхний регистр).

std::string upper_string(const std::string& str)
{
    string upper;
    transform(str.begin(), str.end(), std::back_inserter(upper), toupper);
    return upper;
}

std::string::size_type find_str_ci(const std::string& str, const std::string& substr)
{
    return upper(str).find(upper(substr) );
}

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

Кроме того, мне нужно поддержать std :: wstring / wchar_t. Есть идеи?

tolower / toupper в локали также будет работать с широкими строками, поэтому решение, приведенное выше, должно быть таким же применимым (простое изменение std :: string на std :: wstring).

[Edit] Альтернативой, как указывалось, является адаптация вашего собственного нечувствительного к регистру строкового типа из basic_string, указав свои собственные характеристики символа. Это работает, если вы можете принять все строковые поиски, сравнения и т. Д. Без учета регистра для данного типа строки.

8
ответ дан 24 November 2019 в 18:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: