Checking if a string contains a substring, regardless of capitalization [duplicate] - c++

This question already has answers here:
Case insensitive string::find
(5 answers)
Closed last month.
Suppose I have some string, str.
I was to check if str contains a keyword: "samples"
However, "samples" could be in any form of capitalization such as: "Samples" "SamPleS", "SAMPLES".
this is what I am trying:
string str = "this is a FoO test";
if (str.find("foo") != std::string::npos){
std::cout << "WORKS";
}
This does not detect the "FoO" substring. Is there some sort of argument I can pass through to ignore capitalization? Or should I be using something else entirely?

There are a variety of options.
Using boost::algorithm::ifind_first.
First include <boost/algorithm/string/find.hpp> and <string>.
Then use ifind_first as follows.
std::string str = ...;
std::string subStr = ...;
boost::iterator_range<std::string::const_iterator> rng;
rng = boost::ifind_first(str, subStr);
Using char_traits.
struct ci_char_traits : public char_traits<char>
{
static bool eq(char c1, char c2) { return toupper(c1) == toupper(c2); }
static bool ne(char c1, char c2) { return toupper(c1) != toupper(c2); }
static bool lt(char c1, char c2) { return toupper(c1) < toupper(c2); }
static int compare(const char* s1, const char* s2, size_t n)
{
while( n-- != 0 )
{
if( toupper(*s1) < toupper(*s2) ) return -1;
if( toupper(*s1) > toupper(*s2) ) return 1;
++s1; ++s2;
}
return 0;
}
static const char* find(const char* s, int n, char a)
{
while(n-- > 0 && toupper(*s) != toupper(a))
{
++s;
}
return s;
}
};
typedef std::basic_string<char, ci_char_traits> ci_string;
Then you can use it as follows.
ci_string str = ...;
std::string subStr = ...;
auto pos = str.find(subStr.c_str());
Note that the problem with this is the fact that you need to use the c_str function when calling the find function or when assigning a ci_string to a std::string or when assigning a std::string to a ci_string.
Use std::search with a custom predicate
As suggested in the article Case insensitive std::string.find().

Related

Comparator case insensitive string set

I was just checking effective STL(book) example for set comparator to implement case insensitive set, but i am facing problems(visual studio says comaparator not valid but it worked in ideone). I am sure there is something wrong in my implementation even then it is little confusing what is right.
Below is the code
#include <iostream>
#include <set>
#include <map>
#include <algorithm>
using namespace std;
inline int ciCharCompare(char c1, char c2) // case-insensitively compare chars
{ // c1 and c2, returning -1 if c1 < c2,
//0 if c1==c2, and 1 if c1 > c2
int Ic1 = tolower(static_cast<unsigned char>(c1)); //see below for
int Ic2 = tolower(static_cast<unsigned char>(c2)); // info on these
if (Ic1 < Ic2) return -1;
if (Ic1 > Ic2) return 1;
return 0;
}
struct CiStringCompare : public std::binary_function<string, string, bool>
{
int ciStringCompareImpl(const string& s1, const string& s2) const
{
auto p = mismatch( //see below for an
s1.begin(), s1.end(), //explanation of why
s2.begin(), //we need not2;see
not2(ptr_fun(ciCharCompare))); //Item 41 for why we
// need ptr_fun
if (p.first == s1.end()) { //if true, either s1 and
if (p.second == s2.end()) return 0; // s2 are equal or
else return -1; //s1 is shorter than s2
}
return ciCharCompare(*p.first, *p.second);
}
int xxxx(const string& s1, const string& s2) const
{
if (s1.size() <= s2.size()) return ciStringCompareImpl(s1, s2);
else return -ciStringCompareImpl(s2, s1);
}
int operator()(const string& str1, const string& str2) const {
return xxxx(str1, str2);
}
};
class DifferenceBetweenEquivalenceandEquality
{
// Comparator functor
set<string, CiStringCompare> s;
public:
DifferenceBetweenEquivalenceandEquality(const vector<string>& v)
{
for (const auto& x : v)
s.insert(x);
}
void print() {
for(const auto& x: s)
cout << x << endl;
}
};
int main()
{
DifferenceBetweenEquivalenceandEquality dbee({ "STL","stl","aaa","bbb" });
dbee.print();
return 0;
}
It is working fine in ideone though, so im confused as to what makes a comparator valid and how the equality, equivalence and ordering effect by each of these
For example :: In normal set i get
STL stl aaa bbb
but using the below comparator i get
aaa bbb stl STL
inline string toLowerCase(const string& str) {
string res(str);
int i;
for (i = 0; i < (int)res.size(); i++)
res[i] = (char)tolower(res[i]);
return res;
}
class NormalComparator
{
public:
bool operator()(const string& s1, const string& s2)
{
return toLowerCase(s1) < toLowerCase(s2) ||
!(toLowerCase(s2) < toLowerCase(s1)) && s1 < s2;
}
};

Is there a built in function for std::string in C++ to compare two strings alphabetically when either string can be uppercase or lowercase?

I know for C++ that basic comparative operators can accomplish the task if both words are either entirely lower or entirely upper case. I have an array of strings and letters can vary from lower to upper. Here is a small examle of the kind of strings I can be working with:
"ABUNDANT LIFE CH"
"NEW LIFE WMN MNSTRY"
"NEW LIFE ASSEMBLY"
I know in Java there exists the function String.compareToIgnoreCase(). Is there a C++ equivalent of this function?
I don't know of any case-insensitive functions in the standard library, but you can specify a custom predicate for std::equal:
std::string a("hello");
std::string b("HELLO");
std::cout << std::equal(a.begin(), a.end(), b.begin(),
[] (const char& a, const char& b)
{
return (std::tolower(a) == std::tolower(b));
});
For a solution which takes locale into account, refer to Case insensitive std::string.find().
#include <locale>
template<typename charT = std::string::value_type>
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_;
};
int main()
{
std::string a("hello");
std::string b("HELLO");
std::cout << std::equal(a.begin(), a.end(), b.begin(),
my_equal<>(std::locale()));
}
Yes there is a case insensitive way to compare strings in C++. The key is that std::string is a template:
template <class charT,
class traits = char_traits<charT>,
class Allocator = allocator<charT>>
class basic_string;
The traits here control how the charT's relate to each other. For normal std::string, they do what you'd expect, but we can just write our own traits that are case insensitive:
struct case_insensitive_traits
: char_traits<char>
{
static bool eq(char a, char b) { return tolower(a) == tolower(b); }
static bool ne(char a, char b) { return !eq(a, b); }
static bool lt(char a, char b) { return tolower(a) < tolower(b); }
static bool gt(char a, char b) { return tolower(a) > tolower(b); }
static int compare(const char* a, const char* b, size_t n)
{
for (size_t i = 0; i < n; ++i) {
int delta = tolower(a[i]) - tolower(b[i]);
if (delta != 0) return delta;
}
return 0;
}
static const char* find(const char* s, size_t n, char c)
{
c = tolower(c);
for (size_t i = 0; i < n; ++i, ++s) {
if (tolower(*s) == c) return s;
}
return nullptr;
}
};
With that:
using case_insensitive_string = std::basic_string<char, case_insensitive_traits>;
case_insensitive_string a{"hello"};
case_insensitive_string b{"hElLo"};
assert(a == b);
You can use Boost String Algorithms:
#include <string>
#include <cassert>
#include <boost/algorithm/string.hpp>
int main() {
std::string s { "Test" };
assert(boost::iequals(s, "TEST"));
}
In C++ usually less-than (bool less(type, type)) is used in places of tri-value function compare (int cmp(type, type)). Of course each one of them can be trivially defined in terms of the other.
Here's something that can easily be plugged into STL algorithms:
template<class String>
struct ciless {
locale l_;
explicit ciless(locale l = locale()) : l_(l) {}
bool operator() (
String const &a
, String const &b) const
{
auto fa = a.begin();
auto fb = b.begin();
while (fa != a.end()
&& fb != b.end()
&& (tolower(*fa, l_) == tolower(*fb, l_)))
{
++fa;
++fb;
}
return
(fa == a.end() && fb != b.end())
|| (
fa != a.end()
&& fb != b.end()
&& tolower(*fa, l_) < tolower(*fb, l_));
}
};
And here's something that can convert less() into java-style compare():
template<class T, class Less = std::less<T>>
struct compare
{
Less l_;
explicit compare(Less l = Less()) : l_(l) {}
int operator() (
T const &a
, T const &b) const
{
if (l_(a, b))
return -1;
if (l_(b, a))
return 1;
return 0;
}
};
Nothing standard, but if you happen to be developing for Windows or have access to a Posix interface you could use the following:
https://msdn.microsoft.com/en-us/library/k59z8dwe.aspx
// didn't run it through a compiler
// but it would look like something like this:
{
using namespace std;
string a = "HELLO"s;
string b = "HelLO"s;
bool bIsMatch = _stricmp(a.c_str(), b.c_str()) == 0; // bIsMatch = true
}
There's a much simpler solution that doesn't involve coding loops (ugh!) or Boost:
string a = "Foo";
string b = "foo"
return !_stricmp(a.str(), b.str());
The strings are converted to char[]s, then compared, case-insensitive. Returns true if they're equal.
(My biased opinion: C++ should provide this functionality as a string method. Forcing millions of users to hand-code loops for such a common operation is disgusting, and introduces unnecessary "noise" into the code.)

How to check if string ends with .txt

I am learning basic C++, and right now I have gotten a string from a user and I want to check if they typed the entire file name (including .txt) or not. I have the string, but how can I check if the string ends with ".txt" ?
string fileName;
cout << "Enter filename: \n";
cin >> fileName;
string txt = fileName.Right(4);
The Right(int) method only works with CString, so the above code does not work. I want to use a regular string, if possible. Any ideas?
Unfortunately this useful function is not in the standard library. It is easy to write.
bool has_suffix(const std::string &str, const std::string &suffix)
{
return str.size() >= suffix.size() &&
str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}
Using boost ends_with predicate:
#include <boost/algorithm/string/predicate.hpp>
if (boost::ends_with(fileName, ".txt")) { /* ... */ }
You've gotten quite a few answers already, but I decided to add yet another:
bool ends_with(std::string const &a, std::string const &b) {
auto len = b.length();
auto pos = a.length() - len;
if (pos < 0)
return false;
auto pos_a = &a[pos];
auto pos_b = &b[0];
while (*pos_a)
if (*pos_a++ != *pos_b++)
return false;
return true;
}
Since you have gotten quite a few answers, perhaps a quick test and summary of results would be worthwhile:
#include <iostream>
#include <string>
#include <vector>
#include <time.h>
#include <iomanip>
bool ends_with(std::string const &a, std::string const &b) {
auto len = b.length();
auto pos = a.length() - len;
if (pos < 0)
return false;
auto pos_a = &a[pos];
auto pos_b = &b[0];
while (*pos_a)
if (*pos_a++ != *pos_b++)
return false;
return true;
}
bool ends_with_string(std::string const& str, std::string const& what) {
return what.size() <= str.size()
&& str.find(what, str.size() - what.size()) != str.npos;
}
bool has_suffix(const std::string &str, const std::string &suffix)
{
return str.size() >= suffix.size() &&
str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}
bool has_suffix2(const std::string &str, const std::string &suffix)
{
bool index = str.find(suffix, str.size() - suffix.size());
return (index != -1);
}
bool isEndsWith(const std::string& pstr, const std::string& substr)
{
int tlen = pstr.length();
int slen = substr.length();
if (slen > tlen)
return false;
const char* tdta = pstr.c_str();
const char* sdta = substr.c_str();
while (slen)
{
if (tdta[tlen] != sdta[slen])
return false;
--slen; --tlen;
}
return true;
}
bool ends_with_6502(const std::string& str, const std::string& end) {
size_t slen = str.size(), elen = end.size();
if (slen <= elen) return false;
while (elen) {
if (str[--slen] != end[--elen]) return false;
}
return true;
}
bool ends_with_rajenpandit(std::string const &file, std::string const &suffix) {
int pos = file.find(suffix);
return (pos != std::string::npos);
}
template <class F>
bool test(std::string const &label, F f) {
static const std::vector<std::pair<std::string, bool>> tests{
{ "this is some text", false },
{ "name.txt.other", false },
{ "name.txt", true }
};
bool result = true;
std::cout << "Testing: " << std::left << std::setw(20) << label;
for (auto const &s : tests)
result &= (f(s.first, ".txt") == s.second);
if (!result) {
std::cout << "Failed\n";
return false;
}
clock_t start = clock();
for (int i = 0; i < 10000000; i++)
for (auto const &s : tests)
result &= (f(s.first, ".txt") == s.second);
clock_t stop = clock();
std::cout << double(stop - start) / CLOCKS_PER_SEC << " Seconds\n";
return result;
}
int main() {
test("Jerry Coffin", ends_with);
test("Dietrich Epp", has_suffix);
test("Dietmar", ends_with_string);
test("Roman", isEndsWith);
test("6502", ends_with_6502);
test("rajenpandit", ends_with_rajenpandit);
}
Results with gcc:
Testing: Jerry Coffin 3.416 Seconds
Testing: Dietrich Epp 3.461 Seconds
Testing: Dietmar 3.695 Seconds
Testing: Roman 3.333 Seconds
Testing: 6502 3.304 Seconds
Testing: rajenpandit Failed
Results with VC++:
Testing: Jerry Coffin 0.718 Seconds
Testing: Dietrich Epp 0.982 Seconds
Testing: Dietmar 1.087 Seconds
Testing: Roman 0.883 Seconds
Testing: 6502 0.927 Seconds
Testing: rajenpandit Failed
Yes, those were run on identical hardware, and yes I ran them a number of times, and tried different optimization options with g++ to see if I could get it to at least come sort of close to matching VC++. I couldn't. I don't have an immediate explanation of why g++ produces so much worse code for this test, but I'm fairly confident that it does.
Use std::string::substr
if (filename.substr(std::max(4, filename.size())-4) == std::string(".txt")) {
// Your code here
}
you can just use another string to verify the extension like this :
string fileName;
cout << "Enter filename: \n";
cin >> fileName;
//string txt = fileName.Right(4);
string ext="";
for(int i = fileName.length()-1;i>fileName.length()-5;i--)
{
ext += fileName[i];
}
cout<<ext;
if(ext != "txt.")
cout<<"error\n";
checking if equals "txt." cause i starts with the length of the filename so ext is filled in the opposite way
bool has_suffix(const std::string &str, const std::string &suffix)
{
std::size_t index = str.find(suffix, str.size() - suffix.size());
return (index != std::string::npos);
}
The easiest approach is probably to verify that the string is long enough to hold ".txt" at all and to see if the string can be found at the position size() - 4, e.g.:
bool ends_with_string(std::string const& str, std::string const& what) {
return what.size() <= str.size()
&& str.find(what, str.size() - what.size()) != str.npos;
}
This is something that, unfortunately enough, is not present in the standard library and it's also somewhat annoying to write. This is my attempt:
bool ends_with(const std::string& str, const std::string& end) {
size_t slen = str.size(), elen = end.size();
if (slen < elen) return false;
while (elen) {
if (str[--slen] != end[--elen]) return false;
}
return true;
}
2 options I can think of beside mentioned ones:
1) regex - prob overkill for this, but simple regexes are nice and readable IMHO
2) rbegin - kind of nice, could be I am missing something, but here it is:
bool ends_with(const string& s, const string& ending)
{
return (s.size()>=ending.size()) && equal(ending.rbegin(), ending.rend(), s.rbegin());
}
http://coliru.stacked-crooked.com/a/4de3eafed3bff6e3
This should do it.
bool ends_with(const std::string & s, const std::string & suffix) {
return s.rfind(suffix) == s.length() - suffix.length();
}
Verify here
Since C++17 you can utilize the path class from the filesystem library.
#include <filesystem>
bool ends_with_txt(const std::string& fileName) {
return std::filesystem::path{fileName}.extension() == ".txt";
}
Here is the "fully self-written" solution:
bool isEndsWith(const std::string& pstr, const std::string& substr) const
{
int tlen = pstr.length();
int slen = substr.length();
if(slen > tlen)
return false;
const char* tdta = pstr.c_str();
const char* sdta = substr.c_str();
while(slen)
{
if(tdta[tlen] != sdta[slen])
return false;
--slen; --tlen;
}
return true;
}

C++: case-insensitive first-n-characters string comparison

My question is similar to this, but I have two strings (as char *) and the task is to replace strnicmp function (avaible only for MS VC) with something like boost::iequals.
Note strnicmp is not stricmp - it only compares first n characters.
Is there any solution simplier than this:
void foo(const char *s1, const char *s2)
{
...
std::string str1 = s1;
std::string str2 = s2;
int n = 7;
if (boost::iequals(str1.substr(0, n), str2)) {
...
}
}
If it's really necessary, write your own function:
bool mystrnicmp(char const* s1, char const* s2, int n){
for(int i=0; i < n; ++i){
unsigned char c1 = static_cast<unsigned char>(s1[i]);
unsigned char c2 = static_cast<unsigned char>(s2[i]);
if(tolower(c1) != tolower(c2))
return false;
if(c1 == '\0' || c2 == '\0')
break;
}
return true;
}
For case insensitivity, you need a custom comparison function
(or functor):
struct EqIgnoreCase
{
bool operator()( char lhs, char rhs ) const
{
return ::tolower( static_cast<unsigned char>( lhs ) )
== ::tolower( static_cast<unsigned char>( rhs ) );
}
};
If I understand correctly, you're checking for a prefix. The
simplest way to do this is:
bool
isPrefix( std::string const& s1, std::string const& s2 )
{
return s1.size() <= s2.size()
&& std::equals( s1.begin(), s1.end(), s2.begin(), EqIgnoreCase() );
}
(Note the check of the sizes. s1 can't be a prefix of s2 if
it it longer than s2. And of course, std::equals will
encounter undefined behavior if called with s1 longer than
s2.)
For a function defined in terms of C strings (character pointers) going "up" to STL strings seems horribly inefficient, but maybe that's totally premature thinking on my part.
I would consider a straight C solution "simpler", but again that depends on one's perspective.
#include <ctype.h>
void foo(const char *s1, const char *s2)
{
size_t i, n = 7;
for(i = 0; i < n; i++)
{
if(tolower(s1[i]) != tolower(s2[i]))
return;
if(s[i] == '\0' && s2[i] == '\0')
break;
}
/* Strings are equal, do the work. */
...
}
This assumes that if both strings end before the length of the prefix has been exhausted, it's a match.
Of course the above assumes ASCII strings where tolower() makes sense.
I suggest to write the function yourselfs, like this:
bool strnicmp2(const char *s, const char *t, size_t n) {
while (n > 0 && *s && *t && tolower(*s) == tolower(*t)) {
++s;
++t;
--n;
}
return n == 0 || !*s || !*t;
}
something like this ought to work..
#include <iostream>
#include <string>
#include <cctype>
#include <cstring>
#include <algorithm>
struct isequal
{
bool operator()(int l, int r) const
{
return std::tolower(l) == std::tolower(r);
}
};
bool istrncmp(const char* s1, const char* s2, size_t n)
{
size_t ls1 = std::strlen(s1);
size_t ls2 = std::strlen(s2);
// this is strict, but you can change
if (ls1 < n || ls2 < n)
return false;
return std::equal(s1, s1 + n, s2, isequal());
}
int main(void)
{
std::cout << istrncmp("fooB", "fooA", 3) << std::endl;
std::cout << istrncmp("fooB", "fooA", 5) << std::endl;
std::cout << istrncmp("fooB", "f1oA", 3) << std::endl;
return 0;
}
I don't know if this counts as simpler or not, but it has fewer lines and speed should be pretty good.
#include <boost/iterator/transform_iterator.hpp>
#include <algorithm>
#include <cctype>
bool equal_insensitive_n( char const *a, char const *b, size_t n ) {
n = std::min( n, std::min( ::strlen( a ) + 1, ::strlen( b ) + 1 ) );
#define tilc(S) boost::make_transform_iterator( (S), ::tolower )
return std::equals( tilc(a), tilc(a) + n, tilc(b) );
#undef tilc
}

Find out if string ends with another string in C++

How can I find out if a string ends with another string in C++?
Simply compare the last n characters using std::string::compare:
#include <iostream>
bool hasEnding (std::string const &fullString, std::string const &ending) {
if (fullString.length() >= ending.length()) {
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
} else {
return false;
}
}
int main () {
std::string test1 = "binary";
std::string test2 = "unary";
std::string test3 = "tertiary";
std::string test4 = "ry";
std::string ending = "nary";
std::cout << hasEnding (test1, ending) << std::endl;
std::cout << hasEnding (test2, ending) << std::endl;
std::cout << hasEnding (test3, ending) << std::endl;
std::cout << hasEnding (test4, ending) << std::endl;
return 0;
}
Use this function:
inline bool ends_with(std::string const & value, std::string const & ending)
{
if (ending.size() > value.size()) return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
Use boost::algorithm::ends_with (see e.g. http://www.boost.org/doc/libs/1_34_0/doc/html/boost/algorithm/ends_with.html ):
#include <boost/algorithm/string/predicate.hpp>
// works with const char*
assert(boost::algorithm::ends_with("mystring", "ing"));
// also works with std::string
std::string haystack("mystring");
std::string needle("ing");
assert(boost::algorithm::ends_with(haystack, needle));
std::string haystack2("ng");
assert(! boost::algorithm::ends_with(haystack2, needle));
Note, that starting from c++20 std::string will finally provide starts_with and ends_with. Seems like there is a chance that by c++30 strings in c++ might finally become usable, if you aren't reading this from distant future, you can use these startsWith/endsWith with C++17:
#if __cplusplus >= 201703L // C++17 and later
#include <string_view>
static bool endsWith(std::string_view str, std::string_view suffix)
{
return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix);
}
static bool startsWith(std::string_view str, std::string_view prefix)
{
return str.size() >= prefix.size() && 0 == str.compare(0, prefix.size(), prefix);
}
#endif // C++17
If you are stuck with older C++, you may use these:
#if __cplusplus < 201703L // pre C++17
#include <string>
static bool endsWith(const std::string& str, const std::string& suffix)
{
return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix);
}
static bool startsWith(const std::string& str, const std::string& prefix)
{
return str.size() >= prefix.size() && 0 == str.compare(0, prefix.size(), prefix);
}
and some extra helper overloads:
static bool endsWith(const std::string& str, const char* suffix, unsigned suffixLen)
{
return str.size() >= suffixLen && 0 == str.compare(str.size()-suffixLen, suffixLen, suffix, suffixLen);
}
static bool endsWith(const std::string& str, const char* suffix)
{
return endsWith(str, suffix, std::string::traits_type::length(suffix));
}
static bool startsWith(const std::string& str, const char* prefix, unsigned prefixLen)
{
return str.size() >= prefixLen && 0 == str.compare(0, prefixLen, prefix, prefixLen);
}
static bool startsWith(const std::string& str, const char* prefix)
{
return startsWith(str, prefix, std::string::traits_type::length(prefix));
}
#endif
IMO, c++ strings are clearly dysfunctional, and weren't made to be used in real world code. But there is a hope that this will get better at least.
I know the question's for C++, but if anyone needs a good ol' fashioned C function to do this:
/* returns 1 iff str ends with suffix */
int str_ends_with(const char * str, const char * suffix) {
if( str == NULL || suffix == NULL )
return 0;
size_t str_len = strlen(str);
size_t suffix_len = strlen(suffix);
if(suffix_len > str_len)
return 0;
return 0 == strncmp( str + str_len - suffix_len, suffix, suffix_len );
}
The std::mismatch method can serve this purpose when used to backwards iterate from the end of both strings:
const string sNoFruit = "ThisOneEndsOnNothingMuchFruitLike";
const string sOrange = "ThisOneEndsOnOrange";
const string sPattern = "Orange";
assert( mismatch( sPattern.rbegin(), sPattern.rend(), sNoFruit.rbegin() )
.first != sPattern.rend() );
assert( mismatch( sPattern.rbegin(), sPattern.rend(), sOrange.rbegin() )
.first == sPattern.rend() );
In my opinion simplest, C++ solution is:
bool endsWith(const std::string& s, const std::string& suffix)
{
return s.rfind(suffix) == std::abs(s.size()-suffix.size());
}
Warning: If the match fails, this will search the entire string backwards before giving up, and thus potentially waste a lot of cycles.
Let a be a string and b the string you look for. Use a.substr to get the last n characters of a and compare them to b (where n is the length of b)
Or use std::equal (include <algorithm>)
Ex:
bool EndsWith(const string& a, const string& b) {
if (b.size() > a.size()) return false;
return std::equal(a.begin() + a.size() - b.size(), a.end(), b.begin());
}
Let me extend Joseph's solution with the case insensitive version (online demo)
#include <string>
#include <cctype>
static bool EndsWithCaseInsensitive(const std::string& value, const std::string& ending) {
if (ending.size() > value.size()) {
return false;
}
return std::equal(ending.crbegin(), ending.crend(), value.crbegin(),
[](const unsigned char a, const unsigned char b) {
return std::tolower(a) == std::tolower(b);
}
);
}
Use std::equal algorithm from <algorithms> with reverse iteration:
std::string LogExt = ".log";
if (std::equal(LogExt.rbegin(), LogExt.rend(), filename.rbegin())) {
…
}
Starting from C++20 ends_with introduced.
you can use string::rfind
The full Example based on comments:
bool EndsWith(string &str, string& key)
{
size_t keylen = key.length();
size_t strlen = str.length();
if(keylen =< strlen)
return string::npos != str.rfind(key,strlen - keylen, keylen);
else return false;
}
the very same as above, here is my solution
template<typename TString>
inline bool starts_with(const TString& str, const TString& start) {
if (start.size() > str.size()) return false;
return str.compare(0, start.size(), start) == 0;
}
template<typename TString>
inline bool ends_with(const TString& str, const TString& end) {
if (end.size() > str.size()) return false;
return std::equal(end.rbegin(), end.rend(), str.rbegin());
}
Check if str has suffix, using below:
/*
Check string is end with extension/suffix
*/
int strEndWith(char* str, const char* suffix)
{
size_t strLen = strlen(str);
size_t suffixLen = strlen(suffix);
if (suffixLen <= strLen) {
return strncmp(str + strLen - suffixLen, suffix, suffixLen) == 0;
}
return 0;
}
I thought it makes sense to post a raw solution that doesn't use any library functions...
// Checks whether `str' ends with `suffix'
bool endsWith(const std::string& str, const std::string& suffix) {
if (&suffix == &str) return true; // str and suffix are the same string
if (suffix.length() > str.length()) return false;
size_t delta = str.length() - suffix.length();
for (size_t i = 0; i < suffix.length(); ++i) {
if (suffix[i] != str[delta + i]) return false;
}
return true;
}
Adding a simple std::tolower we can make this case insensitive
// Checks whether `str' ends with `suffix' ignoring case
bool endsWithIgnoreCase(const std::string& str, const std::string& suffix) {
if (&suffix == &str) return true; // str and suffix are the same string
if (suffix.length() > str.length()) return false;
size_t delta = str.length() - suffix.length();
for (size_t i = 0; i < suffix.length(); ++i) {
if (std::tolower(suffix[i]) != std::tolower(str[delta + i])) return false;
}
return true;
}
Regarding Grzegorz Bazior response. I used this implementation, but original one has bug (returns true if I compare ".." with ".so").
I propose modified function:
bool endsWith(const string& s, const string& suffix)
{
return s.size() >= suffix.size() && s.rfind(suffix) == (s.size()-suffix.size());
}
another option is to use regex. The following code makes the search insensitive to upper/lower case:
bool endsWithIgnoreCase(const std::string& str, const std::string& suffix) {
return std::regex_search(str,
std::regex(std::string(suffix) + "$", std::regex_constants::icase));
}
probably not so efficient, but easy to implement.
Found this nice answer to the similar "startWith"-problem:
How do I check if a C++ std::string starts with a certain string, and convert a substring to an int?
You can adopt the solution to only search at the last place in the string:
bool endsWith(const std::string& stack, const std::string& needle) {
return stack.find(needle, stack.size() - needle.size()) != std::string::npos;
}
This way you can make it short, fast, use standard c++ and make it readable.
If, like me, you need endsWith to check a file extension, you can use the std::filesystem library:
std::filesystem::path("/foo/bar.txt").extension() == ".txt"
bool EndsWith(const std::string& data, const std::string& suffix)
{
return data.find(suffix, data.size() - suffix.size()) != std::string::npos;
}
Tests
#include <iostream>
int main()
{
cout << EndsWith(u8"o!hello!1", u8"o!") << endl;
cout << EndsWith(u8"o!hello!", u8"o!") << endl;
cout << EndsWith(u8"hello!", u8"o!") << endl;
cout << EndsWith(u8"o!hello!o!", u8"o!") << endl;
return 0;
}
Output
0
1
1
1
If you're like me and no so into C++ purism, here's an old skool hybrid. There's some advantage when strings are more than a handful of characters, as most memcmp implementations compare machine words when possible.
You need to be in control of the character set. For example, if this approach is used with utf-8 or wchar type, there's some disadvantage as it won't support character mapping - e.g., when two or more characters are logically identical.
bool starts_with(std::string const & value, std::string const & prefix)
{
size_t valueSize = value.size();
size_t prefixSize = prefix.size();
if (prefixSize > valueSize)
{
return false;
}
return memcmp(value.data(), prefix.data(), prefixSize) == 0;
}
bool ends_with(std::string const & value, std::string const & suffix)
{
size_t valueSize = value.size();
size_t suffixSize = suffix.size();
if (suffixSize > valueSize)
{
return false;
}
const char * valuePtr = value.data() + valueSize - suffixSize;
return memcmp(valuePtr, suffix.data(), suffixSize) == 0;
}
My two cents:
bool endsWith(std::string str, std::string suffix)
{
return str.find(suffix, str.size() - suffix.size()) != string::npos;
}
bool endswith(const std::string &str, const std::string &suffix)
{
string::size_type totalSize = str.size();
string::size_type suffixSize = suffix.size();
if(totalSize < suffixSize) {
return false;
}
return str.compare(totalSize - suffixSize, suffixSize, suffix) == 0;
}