不区分大小写的std :: string.find()


68

我正在使用std::stringfind()方法来测试字符串是否是另一个的子字符串。现在,我需要相同内容的不区分大小写的版本。对于字符串比较,我总是可以转向,stricmp()但似乎没有stristr()

我已经找到了各种答案,并且大多数建议Boost在我的情况下不能选择使用哪个答案。另外,我需要支持std::wstring/ wchar_t。有任何想法吗?


1
关于这个主题有一个Gotw:gotw.ca/gotw/029.htm
AlexandreC。2010年

1
stristr不在那儿,而是“ char * strcasestr(const char * haystack,const char * needle);” 在那儿。这样可以吗?
纳西尔2015年

@Nasir,strcasestr在Windows下不可用。
Yuchen Zhong

Answers:


77

您可以使用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;
}

为什么在这里使用模板?
rstackhouse 2014年

@rstackhouse,此处的模板用于支持不同的字符类型(charwchar_t)。
Kirill V. Lyadvinsky 2014年

1
谢谢,基里尔。对于那些像我一样笨拙std::advance( it, offset );的人,请在迭代器的声明之后插入,以从偏移量开始搜索。
拉拉2014年

对于那些不熟悉模板的人(例如我),您是否还可以发布没有模板,没有语言环境的标准版本?仅wstring举例来说,@ KirillV.Lyadvinsky?
巴吉(Basj)

1
通话std::toupper实际上是否适用于宽字符?您不需要打电话std::towupper吗?
MiloDC '20

62

新的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找到


如果我想使用相同的函数c在字符串中查找字符,该怎么办?str调用它findStringIC(str, (string)c)不起作用
Enissay 2015年

这种类型的将字符转换为字符串的方法不起作用,您实际上必须创建一个字符串对象,如std::string(1, 'x') See coliru.stacked-crooked.com/a/af4051dd1d15972e。 如果这样做很多,则可能值得创建不需要创建每次都有一个新对象。
CC。

1
在大多数情况下,最好在tolower()不区分大小写的搜索中使用。甚至Ada也将其更改为小写!Unicode.org可能在某些地方解释了某些原因,但我不知道为什么。
亚历克西斯·威尔克,2016年


5
在这种情况下,不需要模板。对于C ++ 17,您可能想看一下string_view而不是std :: string skebanga.github.io/string-view
CC。

17

为什么不使用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;
}

8
通常,除非为Boost标记了C ++问题,否则假定Boost不是一个选择。
kayleeFrye_onDeck

16

为什么不在调用之前将两个字符串都转换为小写find()

降低

注意:


14
因为它对于较大的字符串非常低效。
2012年

1
如果您的软件曾经需要本地化,这也不是一个好主意。参见土耳其测试:haacked.com/archive/2012/07/05/…–
巴特

xD简而言之,对于C ++ 17以来处理的标准库来说,在C ++上对未编码为ANSI的任何东西进行基本的大写和小写操作时,您将发现的参数将使您不知所措。
kayleeFrye_onDeck

8

由于您是在进行子字符串搜索(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即可)。

[编辑]指出的另一种方法是,通过指定自己的字符特征,从basic_string改写您自己的不区分大小写的字符串类型。如果您可以接受所有字符串搜索,比较等,且对于给定的字符串类型不区分大小写,则此方法有效。



2

提供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库

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

在此示例中,您应注意搜索词没有任何正则表达式特殊字符。


1
“ ...我找到了各种各样的答案,并且大多数建议使用Boost,这对我来说不是一个选择”
jww 2014年

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

我喜欢Kiril V. LyadvinskyCC的回答。但是我的问题不仅仅是不区分大小写。我需要一个懒惰的Unicode支持命令行参数解析器有可能有特殊字符用于格式化alphanum关键字我正在寻找对抗,例如,基础串的字母数字字符串搜索打交道时,可以排除假阳性/阴性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());
}

1
最后三行代码应返回(extracted_match.length()== subString.length());
SJHowe

“应该”在措词上可能有点强,但我同意这是一种改进!:) Ty&更新^ _ ^
kayleeFrye_onDeck

-2

wxWidgets具有非常丰富的字符串API wxString

可以做到的(使用大小写转换方式)

int Contains(const wxString& SpecProgramName, const wxString& str)
{
  wxString SpecProgramName_ = SpecProgramName.Upper();
  wxString str_ = str.Upper();
  int found = SpecProgramName.Find(str_);
  if (wxNOT_FOUND == found)
  {
    return 0;
  }
  return 1;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.