Я использую std::string
find()
метод, чтобы протестировать, если строка является подстрокой другого. Теперь мне нужна нечувствительная к регистру версия того же самого. Для сравнения строк я могу всегда обращаться к stricmp()
но, кажется, нет a stristr()
.
Я нашел различные ответы, и большинство предлагает использовать Boost
который не является опцией в моем случае. Кроме того, я должен поддерживать std::wstring
/wchar_t
. Какие-либо идеи?
Вы можете использовать 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;
}
Если вы хотите «реальное» сравнение согласно правилам Unicode и локали, используйте класс ICU Collator
.
Почему бы просто не преобразовать обе строки в нижний регистр перед вызовом find ()
?
Примечание:
Поскольку вы выполняете поиск по подстрокам (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, указав свои собственные характеристики символа. Это работает, если вы можете принять все строковые поиски, сравнения и т. Д. Без учета регистра для данного типа строки.