web-dev-qa-db-ja.com

大文字と小文字を区別しないstd :: string.find()

私は_std::string_のfind()メソッドを使用して、文字列が別の部分文字列かどうかをテストしています。今、私は同じものの大文字と小文字を区別しないバージョンが必要です。文字列の比較では、常にstricmp()を使用できますが、stristr()が存在しないようです。

さまざまな答えを見つけましたが、ほとんどの場合、Boostを使用することをお勧めしますが、これは私の場合はオプションではありません。さらに、_std::wstring_/_wchar_t_をサポートする必要があります。何か案は?

54
wpfwannabe

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

新しい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 にあります。

47
CC.

なぜfind()を呼び出す前に両方の文字列を小文字に変換しないのですか?

tolower

通知:

  • 長い文字列には非効率的です。
  • 国際化の問題 に注意してください。
12
DavidS

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

要素(文字)検索ではなく部分文字列検索(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) );
}

これは迅速な解決策(悲観的な領域への境界線)ではありませんが、私が知っている唯一の方法です。また、効率が心配な場合は、大文字と小文字を区別しない独自の部分文字列Finderを実装するのはそれほど難しくありません。

さらに、std :: wstring/wchar_tをサポートする必要があります。何か案は?

ロケールのtolower/toupperはワイド文字列でも機能するため、上記の解決策は同じように適用する必要があります(std :: stringをstd :: wstringに簡単に変更します)。

[編集]別の方法として、指摘されているように、独自の文字特性を指定することにより、basic_stringから独自の大文字と小文字を区別しない文字列型を適応させることができます。これは、特定の文字列タイプに対して大文字と小文字を区別しないように、すべての文字列検索、比較などを受け入れることができる場合に機能します。

8
stinky472

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
}

または完全な 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;
}

この例では、検索ワードに正規表現の特殊文字が含まれていないことに注意する必要があります。

3
Boris Ivanov

Unicodeとロケールのルールに従って「実際の」比較が必要な場合は、 ICUのCollatorクラス を使用します。

2
Philipp

Kiril V. Lyadvinsky および [〜#〜] cc [〜#〜] の回答が大好きです。しかし、私の問題は、大文字と小文字を区別しないよりも少し具体的でした。検索対象の英数字キーワードをフォーマットするために使用されるベース文字列に特殊文字が含まれる可能性がある英数字文字列検索を処理するときに、偽陽性/陰性を排除できるレイジーユニコードサポートのコマンドライン引数パーサーが必要でした、例えば_Wolfjäger_は_jäger_とは一致しませんが、_<jäger>_は一致する必要があります。

基本的に、英数字の正確な長さの一致に対する追加の処理を備えたKiril/CCの答えにすぎません。

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

少し汚れていますが、短くて速いです。

0
ziomq1991