c++ string stl case-insensitive wstring

c++ - Case insensitive std:: string.find()



stl case-insensitive (9)

¿Por qué no convertir ambas cadenas a minúsculas antes de llamar a find() ?

tolower

Darse cuenta:

Estoy usando el método find() std::string para probar si una cadena es una subcadena de otra. Ahora necesito la versión insensible del caso de la misma cosa. Para la comparación de cadenas siempre puedo recurrir a stricmp() pero no parece haber un stristr() .

He encontrado varias respuestas y la mayoría sugiero usar Boost que no es una opción en mi caso. Además, necesito soportar std::wstring / wchar_t . ¿Algunas ideas?


¿Por qué no usar 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; }


El nuevo estilo de C ++ 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() ); }

La explicación de la búsqueda cplusplus.com se puede encontrar en cplusplus.com .


Me encantan las respuestas de Kiril V. Lyadvinsky y CC . pero mi problema era un poco más específico que la simple insensibilidad a los casos; Necesitaba un analizador de argumentos de línea de comandos compatible con Unicode que pudiera eliminar falsos positivos / negativos al tratar con búsquedas de cadenas alfanuméricas que podrían tener caracteres especiales en la cadena base utilizada para dar formato a las palabras clave Wolfjäger que estaba buscando, por ejemplo, Wolfjäger no Wolfjäger t coincide con jäger pero <jäger> debería.

Básicamente es solo la respuesta de Kiril / CC con un manejo adicional para las coincidencias alfanuméricas de longitud exacta.

/* Undefined behavior when a non-alpha-num substring parameter is used. */ bool find_alphanum_string_CI(const std::wstring& baseString, const std::wstring& subString) { /* Fail fast if the base string was smaller than what we''re looking for */ if (subString.length() > baseString.length()) return false; auto it = std::search( baseString.begin(), baseString.end(), subString.begin(), subString.end(), [](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); } ); if(it == baseString.end()) return false; size_t match_start_offset = it - baseString.begin(); std::wstring match_start = baseString.substr(match_start_offset, std::wstring::npos); /* Typical special characters and whitespace to split the substring up. */ size_t match_end_pos = match_start.find_first_of(L" ,<.>;:/?/'/"[{]}=+-_)(*&^%$#@!~`"); /* Pass fast if the remainder of the base string where the match started is the same length as the substring. */ if (match_end_pos == std::wstring::npos && match_start.length() == subString.length()) return true; std::wstring extracted_match = match_start.substr(0, match_end_pos); return (extracted_match.length() == subString.length()); }


Podría usar std::search con un predicado personalizado.

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



También tiene sentido proporcionar la versión Boost: esto modificará las cadenas originales.

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

o usando la biblioteca perfecta de boost 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; }

En este ejemplo, debe prestar atención a que su palabra de búsqueda no tenga caracteres especiales de expresiones regulares.


Ya que está haciendo búsquedas de subcadenas (std :: string) y no búsquedas de elementos (caracteres), desafortunadamente no hay una solución existente que yo sepa que esté accesible de inmediato en la biblioteca estándar para hacer esto.

Sin embargo, es bastante fácil de hacer: simplemente convierta ambas cadenas a mayúsculas (o ambas a minúsculas, en este ejemplo elegí mayúsculas).

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

Esta no es una solución rápida (que limita con el territorio de pesimismo), pero es la única que conozco de forma remota. Tampoco es tan difícil implementar su propio buscador de subcadenas que no distingue entre mayúsculas y minúsculas si le preocupa la eficiencia.

Además, necesito soportar std :: wstring / wchar_t. ¿Algunas ideas?

tolower / toupper in locale también funcionará en cadenas anchas, por lo que la solución anterior debería ser igual de aplicable (cambio simple std :: string a std :: wstring).

[Editar] Una alternativa, como se señaló, es adaptar su propio tipo de cadena insensible a mayúsculas y minúsculas de basic_string especificando sus propios rasgos de carácter. Esto funciona si puede aceptar que todas las búsquedas de cadenas, comparaciones, etc. no distingan entre mayúsculas y minúsculas para un tipo de cadena dado.


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

Un poco sucio, pero corto y rápido.