Find string in vector of strings case insensitive c++

◇◆丶佛笑我妖孽 提交于 2021-02-15 05:57:15

问题


I have

std::vector<std::string> vec;
std::string myString; 

and I need to find out if myString is in vec using case insensitive comaprisons.

I know I can use

find(vec.begin(), vec.end(), myString) != vec.end())

to answer the question "is myString in vec?" but that will do case sensitive comparisons. I need case insensitive comparisons.

The position is not important, I just want to know if myString is in vec or not.


回答1:


Or, for a much smaller and easier-to-read solution, Boost!

// #include <algorithm>
// #include <boost/algorithm/string/predicate.hpp>

const auto it = std::find_if(
   std::begin(vec),
   std::end(vec),
   [&myString](const auto& str) { return boost::iequals(myString, str); }
);

const bool found = (it != std::end(vec));



回答2:


You need to use std::tolower and std::find_if:

    std::vector<std::string> vec = {"ALF", "B"};
    std::string toSearch = "Alf";
    auto itr = std::find_if(vec.begin(), vec.end(),
                [&](auto &s) {
                    if ( s.size() != toSearch.size() )
                        return false;
                    for (size_t i = 0; i < s.size(); ++i)
                        if (::tolower(s[i]) == ::tolower(toSearch[i]))
                            return true;
                    return false;
                }
    );
    if ( itr != vec.end()) {
        std::cout << *itr << std::endl;
    }



回答3:


You need to use std::find_if and provide a custom comparator. To achieve case insensitive comparison I would advise you to convert both strings you want to compare to a common case: lower or upper. That would lead to a code like the following:

auto ret = std::find_if(vec.begin(), vec.end(),
    [&myString](const std::string& s) {
        if (s.size() != myString.size())
            return false;
        return std::equal(s.cbegin(), s.cend(), myString.cbegin(), myString.cend(), [](auto c1, auto c2) { return std::toupper(c1) == std::toupper(c2); });
    });

This will return an iterator which will be vec.end() if no occurrence of myString was found. You can do whatever you please with that iterator (including comparing it to vec.end() to know if you found your string).

Bonus: running minimal example on Coliru




回答4:


You may use std::find_if, an inline lambda and std::tolower to make the comparison:

//Computing the lower version of mystring
std::string my_string_lower;
my_string_lower.reserve(mystring.size());
std::transform(mystring.begin(), mystring.end(), std::back_inserter(my_string_lower), ::tolower);

// Checking if it is exist in the vector:
auto is_exist = std::find_if(vec.begin(), vec.end(), [&my_string_lower](std::string item){
    //Transform the each vector item to lower temporally 
    std::transform(item.begin(), item.end(), item.begin(), ::tolower);
    return mystring==item;
}) != vec.end();

if you are going to search many times in the same vetor of string, it would be better if you compute it once:

//Computing the lower version of the whole vector
std::vector<std::string> vec_lower;
vec_lower.reserve(vec.size());
std::transform(vec.begin(), vec.end(), std::back_inserter(vec_lower),[](std:string item){
    std::transform(item.begin(), item.end(), item.begin(), ::tolower);
    return item;
});

//Computing the lower version of mystring
std::string my_string_lower;
my_string_lower.reserve(mystring.size());
std::transform(mystring.begin(), mystring.end(), std::back_inserter(my_string_lower), ::tolower);

// Checking if it is exist in the lower version of the vector:
auto is_exist = std::find_if(vec_lower.begin(), vec_lower.end(), [&my_string_lower](const std::string& item){
    return mystring==item;
}) != vec_lower.end();



回答5:


template <class T>
long VecFindIgnoreCase( const std::vector< T >& vec, const std::string& sFind ) {
    return VecFindIgnoreCase( vec, sFind.c_str() );
}

template <class T>
long VecFindIgnoreCase( const std::vector< T >& vec, const char* sFind )
{
    for ( std::vector< T >::const_iterator iter = vec.begin(); iter != vec.end(); ++iter )
        if ( _stricmp( (*iter).c_str(), sFind ) == 0 )
            return (long)std::distance( vec.begin(), iter );
    return -1;
}

template <class T>
long VecFindIgnoreCase( const std::vector< T >& vec, const std::wstring& sFind ) {
    return VecFindIgnoreCase( vec, sFind.c_str() );
}

template <class T>
long VecFindIgnoreCase( const std::vector< T >& vec, const wchar_t* sFind )
{
    for ( std::vector< T >::const_iterator iter = vec.begin(); iter != vec.end(); ++iter )
        if ( _wcsicmp( (*iter).c_str(), sFind ) == 0 )
            return (long)std::distance( vec.begin(), iter );
    return -1;
}

Use:

#include <string>
#include <vector>

void TestCode()
{
    std::vector< std::string > strvecA;
    std::vector< std::wstring > strvecW;

    strvecA.push_back("abc");
    strvecA.push_back("def");             
    strvecA.push_back("ghi");
    strvecW.push_back(L"abc");
    strvecW.push_back(L"def");
    strvecW.push_back(L"ghi");

    long ind;

    ind = VecFindIgnoreCase( strvecA, "ABC" ); // ind = 0 found
    ind = VecFindIgnoreCase( strvecA, "ghI" ); // ind = 2 found
    ind = VecFindIgnoreCase( strvecA, "Xyz" ); // ind = -1 not found

    ind = VecFindIgnoreCase( strvecW, L"aBc" ); // ind = 0 found
    ind = VecFindIgnoreCase( strvecW, L"DEF" ); // ind = 1 found
    ind = VecFindIgnoreCase( strvecW, L"xyZ" ); // ind = -1 not found

    std::string sFind( "mno" );
    if ( (ind = VecFindIgnoreCase( strvecA, sFind )) >= 0 ) {
        // found at strvecA[ind]
    } else {
        // not found
    }
}


来源:https://stackoverflow.com/questions/36494584/find-string-in-vector-of-strings-case-insensitive-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!