development

대소 문자를 구분하지 않음 std :: string.find ()

big-blog 2021. 1. 9. 11:29
반응형

대소 문자를 구분하지 않음 std :: string.find ()


std::stringfind()방법을 사용하여 문자열이 다른 문자열인지 테스트하고 있습니다. 이제 대소 문자를 구분하지 않는 동일한 버전이 필요합니다. 문자열 비교를 위해 항상 사용할 수 stricmp()있지만 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;
}

새로운 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() );
}

std :: search에 대한 설명은 cplusplus.com 에서 찾을 수 있습니다 .


호출하기 전에 두 문자열을 모두 소문자로 변환하지 않는 이유는 무엇 find()입니까?

낮추다

주의:


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

요소 (문자) 검색이 아닌 하위 문자열 검색 (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] An alternative, as pointed out, is to adapt your own case-insensitive string type from basic_string by specifying your own character traits. This works if you can accept all string searches, comparisons, etc. to be case-insensitive for a given string type.


Also make sense to provide Boost version: This will modify original strings.

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

or using perfect boost xpression library

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

In this example you should pay attention that your search word don't have any regex special characters.


If you want “real” comparison according to Unicode and locale rules, use ICU’s Collator class.


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

A little bit dirty, but short & fast.


I love the answers from Kiril V. Lyadvinsky and CC. but my problem was a little more specific than just case-insensitivity; I needed a lazy Unicode-supported command-line argument parser that could eliminate false-positives/negatives when dealing with alphanumeric string searches that could have special characters in the base string used to format alphanum keywords I was searching against, e.g., Wolfjäger shouldn't match jäger but <jäger> should.

It's basically just Kiril/CC's answer with extra handling for alphanumeric exact-length matches.

/* 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());
}

ReferenceURL : https://stackoverflow.com/questions/3152241/case-insensitive-stdstring-find

반응형