I want it to consider anything that isn't an alphabet character to be a delimiter. How can I do this?
You can't. The default delimiter is \n:
while (std::getline (std::cin, str) // '\n' is implicit
For other delimiters, pass them:
while (std::getline (std::cin, str, ' ') // splits at a single whitespace
However, the delimiter is of type char, thus you can only use one "split-character", but not what not to match.
If your input already happens to be inside a container like std::string, you can use find_first_not_of or find_last_not_of.
In your other question, are you sure you have considered all answers? One uses istream::operator>>(std::istream&, <string>), which will match a sequence of non-whitespace characters.
You don't. getline is a simple tool for a simple job. If you need something more complex, then you need to use a more complex tool, like RegEx's or something.
You can't do what you want using std::getline(), but you can roll your own. Here's a getline variant that let's you specify a predicate (function, functor, lambda if it's C++11) to indicate if a character is a delimiter along with a couple overloads that let you pass in a string of delimiter characters (kind of like strtok()):
#include <functional>
#include <iostream>
#include <string>
using namespace std;
template <typename Predicate>
istream& getline_until( istream& is, string& str, Predicate pred)
{
bool changed = false;
istream::sentry k(is,true);
if (bool(k)) {
streambuf& rdbuf(*is.rdbuf());
str.erase();
istream::traits_type::int_type ch = rdbuf.sgetc(); // get next char, but don't move stream position
for (;;ch = rdbuf.sgetc()) {
if (istream::traits_type::eof() == ch) {
is.setstate(ios_base::eofbit);
break;
}
changed = true;
rdbuf.sbumpc(); // move stream position to consume char
if (pred(istream::traits_type::to_char_type(ch))) {
break;
}
str.append(1,istream::traits_type::to_char_type(ch));
if (str.size() == str.max_size()) {
is.setstate(ios_base::failbit);
break;
}
}
if (!changed) {
is.setstate(ios_base::failbit);
}
}
return is;
}
// a couple of overloads (along with a predicate) that allow you
// to pass in a string that contains a set of delimiter characters
struct in_delim_set : unary_function<char,bool>
{
in_delim_set( char const* delim_set) : delims(delim_set) {};
in_delim_set( string const& delim_set) : delims(delim_set) {};
bool operator()(char ch) {
return (delims.find(ch) != string::npos);
};
private:
string delims;
};
istream& getline_until( istream& is, string& str, char const* delim_set)
{
return getline_until( is, str, in_delim_set(delim_set));
}
istream& getline_until( istream& is, string& str, string const& delim_set)
{
return getline_until( is, str, in_delim_set(delim_set));
}
// a simple example predicate functor
struct is_digit : unary_function<char,bool>
{
public:
bool operator()(char c) const {
return ('0' <= c) && (c <= '9');
}
};
int main(int argc, char* argv[]) {
string test;
// treat anything that's not a digit as end-of-line
while (getline_until( cin, test, not1(is_digit()))) {
cout << test << endl;
}
return 0;
}
Related
Can you help me with this aproach :
The thing is, we need to do a case-insensitive search for the keywords in a string (for a function which return true is if any of the keyword is found in the string, elsewise false)
So I am using this piece of code:
std::transform(string.begin(), string.end(), string.begin(), ::toupper);
std::transform(keywords.begin(), keywords.end(), keywords.begin(), ::toupper);
std::istringstream iss(keywords);
std::string word;
while(iss >> word) {
if(string.find(word) != std::string::npos)
return true;
}
return false;
The problem with this is that it creates unnecessary copies of the existing data. Can there be a better approach to it?
First of all for making this more reuseable creating an object responsible for holding the keyword data would be preferrable. You can use std::string_views, std::pair<std::string::const_iterator, std::string::const_iterator> or something similar to avoid making a copy of the string data for the keywords and using std::search to find the keywords allows you to prevent having to copy the string to convert it to upper case for a search while also keeping benefit of converting the keywords to upper case:
class KeywordSearch
{
std::vector<std::string_view> m_keywords;
std::string m_keywordData;
public:
KeywordSearch(std::string&& keywords)
: m_keywordData(std::move(keywords))
{
auto pos = m_keywordData.begin();
auto const end = m_keywordData.end();
std::for_each(pos, end, [](char& c) { c = std::toupper(c); });
pos = std::find_if(pos, end, [](unsigned char c) { return !std::isspace(c); });
while (pos != end)
{
auto keywordEnd = std::find_if(pos + 1, end, [](unsigned char c) { return std::isspace(c); });
m_keywords.emplace_back(pos, keywordEnd);
pos = std::find_if(keywordEnd, end, [](unsigned char c) { return !std::isspace(c); });
}
}
// allow only move for now; copy would require an update of m_keywords
KeywordSearch(KeywordSearch&&) noexcept = default;
KeywordSearch& operator=(KeywordSearch&&) noexcept = default;
bool operator()(std::string const& haysack) const
{
for (auto const& keyword : m_keywords)
{
if (std::search(haysack.begin(), haysack.end(), keyword.begin(), keyword.end(),
[](char haysackChar, char keywordChar)
{
return std::toupper(haysackChar) == keywordChar;
}) != haysack.end())
{
return true;
}
}
return false;
}
};
void Test(KeywordSearch const& search, std::string const& str)
{
std::cout << (search(str) ? " keyword found in '" : "keyword not found in '") << str << "'\n";
}
int main() {
KeywordSearch search("foo bar baz");
Test(search, "NoFoOB");
Test(search, "barblabla");
Test(search, "babbba");
Test(search, "hello world");
Test(search, "hello wobaz");
}
Yes, maybe. If you want to avoid copies, then you can use an iterator.
C++ offers a functionality to iterate over paterrns in a string. This is the std::sregex_token_iterator. You can read here about that. You can either define a "positive" pattern, so, what you are looking for. Example: "\w+" will look for words. Or, you do a "negative" search, meaning, specify the separator (e.g., ' ' as a std::regex) and add "-1" as fourth parameter.
Then you can iterate over all keywords.
As for the case insenitivity. Do the conversion one time. I will not even show it in my below example.
First I created a small demo, where I print out the keywords that have been found in the given std::string.
#include <iostream>
#include <regex>
#include <string>
#include <iterator>
#include <algorithm>
const std::regex re{ R"(\w+)" };
int main() {
// Keys
const std::string keys{ "abc def ghi jkl" };
// Search string
std::string s{ "abcxxxghixxx" };
std::copy_if(std::sregex_token_iterator(keys.begin(), keys.end(), re), {},
std::ostream_iterator<std::string>(std::cout,"\n"),
[&s](const std::string& k) {return s.find(k) != std::string::npos; });
}
This approach can be taken over to build your needed function.
One of many possible solutions:
#include <iostream>
#include <regex>
#include <string>
#include <iterator>
#include <algorithm>
const std::regex re{ R"(\w+)" };
bool isAnyKeyWordInString(const std::string& keys, const std::string& s) {
bool result{};
std::for_each(std::sregex_token_iterator(keys.begin(), keys.end(), re), {},
[&](const std::string& k) {result |= (s.find(k) != std::string::npos); });
return result;
}
int main() {
// Keys
const std::string keys{ "abc def ghi jkl" };
// Search string
std::string s{ "abcxxxghixxx" };
// Evaluate
if (isAnyKeyWordInString(keys, s))
std::cout << "At least one key-word found\n";
else
std::cout << "No Keyword found\n";
}
this is fast!!!
but you have to keep changing the parameters depending on your requirement, for example: const int keyword_len = (int)keyword.length(); here i'm casting the unsigned long int to int.
also i'm not converting my searchable string to upper case before my conditional statement because its faster this way instead of looping through the string when i need to search.
when it comes to complexity it's O(n)+k where k<<n, because i'm not comparing the entire keyword size to the searchable string.
P.s. sorry for not making this code ideally reusable, but it is intractable.
#include <cctype>
#include <iostream>
#include <string>
int main(){
std::string searchable_string;
std::string keyword;
std::cout<<"enter the searchable string : "<<std::endl;
std::getline(std::cin, searchable_string);
std::cout<<"enter the keyword you are looking for : "<<std::endl;
std::getline(std::cin, keyword);
const int keyword_len = (int)keyword.length();
const int searchable_string_len = (int)searchable_string.length();
int upper_keyword[keyword_len];
int i = 0;
for(;i<keyword_len;){
upper_keyword[i] = toupper(x);
i++;
}
i = 0;
int j;
keeplooking:
j = 0;
comparetokeyword:
if(upper_keyword[j]==toupper(searchable_string[i+j])){
j++;
if(j <= (keyword_len)-1) goto comparetokeyword;
else {
//'i' is the relative position of your keyword.
std::cout<<"found keyword at: "<<std::endl<<i<<std::endl;
std::cout<<"do you want to keep looking for more(enter '1' for Yes and '0' for No) :"<<std::endl;
bool x;
std::cin>>x;
if(x){
i++;
if(i<searchable_string_len) goto keeplooking;
else goto terminate;
}
}
}
else{
i++;
if(i<searchable_string_len){
goto keeplooking;
}
terminate:
std::cout<<"reached the end."<<std::endl;
}
}
I am using std::string's find() method to test if a string is a substring of another. Now I need case insensitive version of the same thing. For string comparison I can always turn to stricmp() but there doesn't seem to be a stristr().
I have found various answers and most suggest using Boost which is not an option in my case. Additionally, I need to support std::wstring/wchar_t. Any ideas?
You could use std::search with a custom predicate.
#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;
}
The new C++11 style:
#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(),
[](unsigned char ch1, unsigned char ch2) { return std::toupper(ch1) == std::toupper(ch2); }
);
return (it != strHaystack.end() );
}
Explanation of the std::search can be found on cplusplus.com.
why not use 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;
}
Why not just convert both strings to lowercase before you call find()?
tolower
Notice:
Inefficient for long strings.
Beware of internationalization issues.
Since you're doing substring searches (std::string) and not element (character) searches, there's unfortunately no existing solution I'm aware of that's immediately accessible in the standard library to do this.
Nevertheless, it's easy enough to do: simply convert both strings to upper case (or both to lower case - I chose upper in this example).
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) );
}
This is not a fast solution (bordering into pessimization territory) but it's the only one I know of off-hand. It's also not that hard to implement your own case-insensitive substring finder if you are worried about efficiency.
Additionally, I need to support
std::wstring/wchar_t. Any ideas?
tolower/toupper in locale will work on wide-strings as well, so the solution above should be just as applicable (simple change std::string to 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.
If you want “real” comparison according to Unicode and locale rules, use ICU’s Collator class.
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.
#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());
}
The Most Efficient Way
Simple and Fast.
Performance is guaranteed to be linear, with an initialization cost of 2 * NEEDLE_LEN comparisons. (glic)
#include <cstring>
#include <string>
#include <iostream>
int main(void) {
std::string s1{"abc de fGH"};
std::string s2{"DE"};
auto pos = strcasestr(s1.c_str(), s2.c_str());
if(pos != nullptr)
std::cout << pos - s1.c_str() << std::endl;
return 0;
}
wxWidgets has a very rich string API
wxString
it can be done with (using the case conversion way)
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;
}
I have implemented a simple inputstream manipulator to match the next n chars in an inputstream against a given string. However, I am not sure if this is the best way to do this. Any hints?
class MatchString {
private:
std::string mString;
public:
MatchString(const std::string &str) {
mString = str;
}
std::istream& operator()(std::istream& is) const {
// Allocate a string buffer, ...
char *buffer = new char[mString.length()];
// ... read next n chars into the buffer ...
is.read(buffer, mString.length());
// ... and compare them with given string.
if(strncmp(buffer, mString.c_str(), mString.length())) {
throw MismatchException(mString);
}
delete[] buffer;
return is;
}
};
inline MatchString match(const std::string &str) {
return MatchString(str);
}
inline std::istream& operator>>(std::istream& is, const MatchString& matchStr) {
return matchStr(is);
}
EDIT:
A solution consuming the matched chars could be implemented based on the suggestion of user673679:
class MatchString {
...
std::istream& operator()(std::istream& is) const {
// Match the next n chars.
std::for_each(mString.begin(), mString.end(),
[&](const char c) {
if(is.get() != c) {
throw MismatchException(mString);
}
});
return is;
}
};
How would I implement this if I don't want to consume the chars?
EDIT II:
Here another solution mentioned by fjardon:
class MatchString {
...
std::istream& operator()(std::istream& is) const {
// Match the next n chars.
if(std::mismatch(mString.begin(), mString.end(),
std::istreambuf_iterator<char>(is)).first != mString.end()) {
throw MismatchException(mString);
}
return is;
}
};
EDIT III:
Finally got a working function, that will revert consumption, if string doesn't match:
class MatchString {
...
std::istream& operator()(std::istream& is) const {
// Match the next n chars.
std::streampos oldPos = is.tellg();
if(std::mismatch(mString.begin(), mString.end(),
std::istreambuf_iterator<char>(is)).first != mString.end()) {
is.seekg(oldPos);
throw MismatchException(mString);
}
return is;
}
};
Instead of allocating and copying the whole string from the stream, you could just check one character at a time and avoid allocating the buffer completely:
#include <iostream>
#include <sstream>
#include <string>
auto mString = std::string("foobar");
std::istream& match(std::istream& is) {
for (auto c : mString)
if (c != is.get())
throw std::runtime_error("nope");
return is;
}
int main()
{
auto input = "foobarbaz";
auto stream = std::istringstream(input);
match(stream);
std::cout << "done!" << std::endl;
}
You should also add error checking for is.get() (or .read() in your original code).
I am trying to extract a string from an istream with strings as delimiters, yet i haven't found any string operations with behavior close to such as find() or substr() in istreams.
Here is an example istream content:
delim_oneFUUBARdelim_two
and my goal is to get FUUBAR into a string with as little workarounds as possible.
My current solution was to copy all istream content into a string using this solution for it and then extracting using string operations. Is there a way to avoid this unnecessary copying and only read as much from the istream as needed to preserve all content after the delimited string in case there are more to be found in similar fashion?
You can easily create a type that will consume the expected separator or delimiter:
struct Text
{
std::string t_;
};
std::istream& operator>>(std::istream& is, Text& t)
{
is >> std::skipws;
for (char c: t.t_)
{
if (is.peek() != c)
{
is.setstate(std::ios::failbit);
break;
}
is.get(); // throw away known-matching char
}
return is;
}
See it in action on ideone
This suffices when the previous stream extraction naturally stops without consuming the delimiter (e.g. an int extraction followed by a delimiter that doesn't start with a digit), which will typically be the case unless the previous extraction is of a std::string. Single-character delimiters can be specified to getline, but say your delimiter is "</block>" and the stream contains "<black>metalic</black></block>42" - you'd want something to extract "<black>metallic</black>" into a string, throw away the "</block>" delimiter, and leave the "42" on the stream:
struct Until_Delim {
Until_Delim(std::string& s, std::string delim) : s_(s), delim_(delim) { }
std::string& s_;
std::string delim_;
};
std::istream& operator>>(std::istream& is, const Until_Delim& ud)
{
std::istream::sentry sentry(is);
size_t in_delim = 0;
for (char c = is.get(); is; c = is.get())
{
if (c == ud.delim_[in_delim])
{
if (++in_delim == ud.delim_.size())
break;
continue;
}
if (in_delim) // was part-way into delimiter match...
{
ud.s_.append(ud.delim_, 0, in_delim);
in_delim = 0;
}
ud.s_ += c;
}
// may need to trim trailing whitespace...
if (is.flags() & std::ios_base::skipws)
while (!ud.s_.empty() && std::isspace(ud.s_.back()))
ud.s_.pop_back();
return is;
}
This can then be used as in:
string a_string;
if (some_stream >> Until_Delim(a_string, "</block>") >> whatevers_after)
...
This notation might seem a bit hackish, but there's precedent in Standard Library's std::quoted().
You can see the code running here.
Standard streams are equipped with locales that can do classification, namely the std::ctype<> facet. We can use this facet to ignore() characters in a stream while a certain classification is not present in the next available character. Here's a working example:
#include <iostream>
#include <sstream>
using mask = std::ctype_base::mask;
template<mask m>
void scan_classification(std::istream& is)
{
auto& ctype = std::use_facet<std::ctype<char>>(is.getloc());
while (is.peek() != std::char_traits<char>::eof() && !ctype.is(m, is.peek()))
is.ignore();
}
int main()
{
std::istringstream iss("some_string_delimiter3.1415another_string");
double d;
scan_classification<std::ctype_base::digit>(iss);
if (iss >> d)
std::cout << std::to_string(d); // "3.1415"
}
I am using std::string's find() method to test if a string is a substring of another. Now I need case insensitive version of the same thing. For string comparison I can always turn to stricmp() but there doesn't seem to be a stristr().
I have found various answers and most suggest using Boost which is not an option in my case. Additionally, I need to support std::wstring/wchar_t. Any ideas?
You could use std::search with a custom predicate.
#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;
}
The new C++11 style:
#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(),
[](unsigned char ch1, unsigned char ch2) { return std::toupper(ch1) == std::toupper(ch2); }
);
return (it != strHaystack.end() );
}
Explanation of the std::search can be found on cplusplus.com.
why not use 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;
}
Why not just convert both strings to lowercase before you call find()?
tolower
Notice:
Inefficient for long strings.
Beware of internationalization issues.
Since you're doing substring searches (std::string) and not element (character) searches, there's unfortunately no existing solution I'm aware of that's immediately accessible in the standard library to do this.
Nevertheless, it's easy enough to do: simply convert both strings to upper case (or both to lower case - I chose upper in this example).
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) );
}
This is not a fast solution (bordering into pessimization territory) but it's the only one I know of off-hand. It's also not that hard to implement your own case-insensitive substring finder if you are worried about efficiency.
Additionally, I need to support
std::wstring/wchar_t. Any ideas?
tolower/toupper in locale will work on wide-strings as well, so the solution above should be just as applicable (simple change std::string to 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.
If you want “real” comparison according to Unicode and locale rules, use ICU’s Collator class.
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.
#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());
}
The Most Efficient Way
Simple and Fast.
Performance is guaranteed to be linear, with an initialization cost of 2 * NEEDLE_LEN comparisons. (glic)
#include <cstring>
#include <string>
#include <iostream>
int main(void) {
std::string s1{"abc de fGH"};
std::string s2{"DE"};
auto pos = strcasestr(s1.c_str(), s2.c_str());
if(pos != nullptr)
std::cout << pos - s1.c_str() << std::endl;
return 0;
}
wxWidgets has a very rich string API
wxString
it can be done with (using the case conversion way)
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;
}