Нечувствительность к регистру std::string.find()
Я использую метод std::string
find()
, чтобы проверить, является ли строка подстрокой другого. Теперь мне нужна нечувствительная к регистру версия того же самого. Для сравнения строк я всегда могу обратиться к stricmp()
, но, похоже, не существует stristr()
.
Я нашел несколько ответов, и большинство из них предлагает использовать Boost
, который не является вариантом в моем случае. Кроме того, мне нужно поддерживать std::wstring
/wchar_t
. Любые идеи?
Ответы
Ответ 1
Вы можете использовать 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;
}
Ответ 2
Новый стиль С++ 11:
#include <algorithm>
#include <string>
#include <cctype>
/// Try to find in the Haystack the Needle - ignore case
bool findStringIC(const std::string & strHaystack, const std::string & strNeedle)
{
auto it = std::search(
strHaystack.begin(), strHaystack.end(),
strNeedle.begin(), strNeedle.end(),
[](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }
);
return (it != strHaystack.end() );
}
Объяснение "поиск" можно найти на cplusplus.com.
Ответ 3
Почему бы просто не преобразовать обе строки в нижний регистр, прежде чем вы вызовете find()
?
tolower
Примечание:
Ответ 4
почему бы не использовать Boost.StringAlgo:
#include <boost/algorithm/string/find.hpp>
bool Foo()
{
//case insensitive find
std::string str("Hello");
boost::iterator_range<std::string::const_iterator> rng;
rng = boost::ifind_first(str, std::string("EL"));
return rng;
}
Ответ 5
Поскольку вы выполняете поиск подстроки (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) );
}
Это не быстрое решение (граничащее с территорией пессимизации), но это единственное, что я знаю из рук. Это также не так сложно реализовать свой собственный искатель подстроки, нечувствительный к регистру, если вы обеспокоены эффективностью.
Кроме того, мне нужно поддерживать станд:: wstring/wchar_t. Любые идеи?
tolower/toupper в locale будет работать и с широкоформатными строками, поэтому вышеприведенное решение должно быть таким же применимым (простое изменение std::string to std:: wstring).
[Edit] Альтернатива, как указано, заключается в том, чтобы адаптировать собственный тип строки, не учитывающий регистр, от basic_string, указав свои собственные черты характера. Это работает, если вы можете принять все строковые запросы, сравнения и т.д., Чтобы они не зависели от регистра для заданного типа строки.
Ответ 6
Также имеет смысл предоставить версию Boost: это изменит исходные строки.
#include <boost/algorithm/string.hpp>
string str1 = "hello world!!!";
string str2 = "HELLO";
boost::algorithm::to_lower(str1)
boost::algorithm::to_lower(str2)
if (str1.find(str2) != std::string::npos)
{
// str1 contains str2
}
или используя совершенную ускорить библиотеку xpression
#include <boost/xpressive/xpressive.hpp>
using namespace boost::xpressive;
....
std::string long_string( "very LonG string" );
std::string word("long");
smatch what;
sregex re = sregex::compile(word, boost::xpressive::icase);
if( regex_match( long_string, what, re ) )
{
cout << word << " found!" << endl;
}
В этом примере вы должны обратить внимание, что ваше слово поиска не имеет специальных символов регулярного выражения.
Ответ 7
Если вам нужно "реальное" сравнение в соответствии с правилами Unicode и locale, используйте ICUs Collator
class.
Ответ 8
#include <iostream>
using namespace std;
template <typename charT>
struct ichar {
operator charT() const { return toupper(x); }
charT x;
};
template <typename charT>
static basic_string<ichar<charT> > *istring(basic_string<charT> &s) { return (basic_string<ichar<charT> > *)&s; }
template <typename charT>
static ichar<charT> *istring(const charT *s) { return (ichar<charT> *)s; }
int main()
{
string s = "The STRING";
wstring ws = L"The WSTRING";
cout << istring(s)->find(istring("str")) << " " << istring(ws)->find(istring(L"wstr")) << endl;
}
Немного грязный, но короткий и быстрый.