找出字符串在C ++中是否以另一个字符串结尾


270

如何找出一个字符串是否在C ++中以另一个字符串结尾?

Answers:


211

只需使用以下命令比较最后n个字符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;
}

是的,毫无疑问,这是最好的方法。
诺多林

3
我总是讨厌计算子字符串的索引,这很容易被一个字符串...我宁愿从两个字符串的末尾向后迭代,试图找到不匹配的地方。
xtofl,2009年

17
@诺多林我不同意。这很容易-最好的方法是使用库。C ++标准库做的有用的事情很少,真可惜。
masterxilo 2014年

1
@masterxilo您建议使用哪个库来解决此问题,与(单行)单行函数相比,该库是一个更好的选择吗?
Brandin 2014年

33
@Brandin因为它是这样的基本功能。C ++迫使我们一次又一次地重新编程与任何其他现代计算机语言一样即开即用的功能。人们需要去stackoverflow来解决这个问题这一事实表明存在一个pb。
Conchylicultor '17

175

使用此功能:

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());
}

3
请注意,MSVC10不喜欢此解决方案:std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()在调试模式下,它会抛出:_DEBUG_ERROR("string iterator not decrementable");
remi.chateauneu 2014年

154

使用boost::algorithm::ends_with(请参见例如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));

83

注意,从c ++ 20 std :: string 开始,最终将提供starts_withends_with。似乎到c ++的c ++中的30个字符串可能最终会变得可用,如果您不打算从遥远的将来阅读此内容,则可以使用以下startsWith / endsWith:

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

和一些额外的帮助程序重载:

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

IMO,c ++字符串显然是功能失调的,并不是要在现实世界的代码中使用。但是有希望至少会有所改善。


2
由于str.compare不返回布尔值,因此使用not(“!”)运算符测试“ == 0”并不是很聪明,因为这可能会使读者感到困惑。为了清楚起见,请使用“ ... && str.compare(...)== 0”。
Thomas Tempelmann '17年

@Pavel是否有理由在您的“ startsWith”方法中不使用std :: string :: find?
Maxime Oudot

4
@MaximeOudot当然有!如果您想知道整个字符串是否以某些内容开头,为什么还要搜索整个字符串?换句话说,您可能最终会搜索100mb长的字符串以找到结尾的片段,然后忽略该结果,因为它不在字符串的开头。
Pavel P

1
加“ 1”表示c ++ 30预测。
无辜的旁观者

40

我知道这个问题是C ++的,但是如果有人需要一个好的OL风格的C函数来做到这一点:


/*  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 );
}


25

std::mismatch当用于从两个字符串的末尾向后迭代时,该方法可以达到此目的:

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() );

3
+1。我之前从未注意到过std :: mismatch()-我想知道我从未看过的算法头文件中还有什么……
j_random_hacker,2009年

3
我认为这本身就值得一个SO问题:您是否曾经浏览过可用的stl函数?
xtofl,2009年

2
请注意,这与std::equal:具有相同的要求:您需要预先检查假定的后缀不长于要在其中搜索的字符串。忽略检查会导致未定义的行为。
罗伯·肯尼迪

18

我认为最简单的C ++解决方案是:

bool endsWith(const string& s, const string& suffix)
{
    return s.rfind(suffix) == std::abs(s.size()-suffix.size());
}

10
这相当慢,因为您将搜索整个字符串,s而不仅仅是测试它的结尾!
Alexis Wilke 2013年

2
@nodakai,如果我碰巧有一个1Mb的字符串,它将远远超过纳秒。
Alexis Wilke

我不这么认为……无论如何都需要努力,然后再从头开始。
LtWorf

2
@LtWorf std::string::size()是一个恒定时间操作;它不需要strlen
托马斯

2
如果在suffix.size()== s.size()+ 1的情况下失败了,怎么可能会考虑这种解决方案。显示此onlinegdb.com/S1ITVqKDL的代码片段。如果复杂度无法在所有情况下正常运行,则无关紧要。
c0ntrol

10

让它a是一个字符串和b您要查找的字符串。使用a.substr获得的最后n个字符a,并把它们比作B(其中n是长度b

或使用std::equal(包括<algorithm>

例如:

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());
}

如果以\ r或\ n或两者同时出现在字符串之后,我也如何返回true?谢谢!
sofr,2009年

@Dario:您使用std :: equal()的解决方案很好,使用substr()的解决方案不是很多-除非您使用的是COW字符串(我相信很少有人),substr()意味着创建第二个副本部分字符串,表示涉及动态内存分配。这可能会失败,并且在任何情况下都意味着比其他解决方案使用了更多的内存(并且几乎肯定比其他解决方案要慢)。
j_random_hacker

4

让我用不区分大小写的版本扩展Joseph的解决方案在线演示

static bool EndsWithCaseInsensitive(const std::string& value, const std::string& ending) {
    if (ending.size() > value.size()) {
        return false;
    }
    return std::equal(ending.rbegin(), ending.rend(), value.rbegin(),
        [](const char a, const char b) {
            return tolower(a) == tolower(b);
        }
    );
}

3

和上面一样,这是我的解决方案

 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());
  }

1
为什么starts_with使用'string :: compare'?为什么不std::equal(start.begin(), start.end(), str.begin())呢?
Dmytro Ovdiienko 2016年

仅仅因为starts_with是我需要的第一个。Ends_with稍后添加。
dodjango

3

另一个选择是使用正则表达式。以下代码使搜索对大写/小写不敏感:

bool endsWithIgnoreCase(const std::string& str, const std::string& suffix) {
  return std::regex_search(str,
     std::regex(std::string(suffix) + "$", std::regex_constants::icase));
}

可能效率不高,但易于实现。


对于使用C ++ 11或更高版本的用户,这非常方便。
克莱尔·麦克雷

当心,正则表达式在C ++中可能会非常慢!
mxmlnkn

正则表达式就像...我需要对此投票。我不会,但我应该。
MK。

2

您可以使用string :: rfind

基于注释的完整示例:

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

3
-1。是的,您可以使用它,但是在字符串未以提供的结尾结尾的情况下,它会不必要地变慢-扫描将一直持续到字符串的开头。另外,您无需提及需要进行后续测试以确保结尾匹配于字符串的末尾,而不是字符串的其他地方。
j_random_hacker

我只是放置了所需功能的链接,我认为从文档str.rfind(key,str.length()-key.length(),key.length())可以很容易地做到这一点。
艾哈迈德·赛义德

好的,这很有效-但在这种情况下,string :: find()也可以正常工作。另外,您还需要提及key.length()> str.length()的情况-在这种情况下,您在注释中建议的代码将崩溃。如果您使用此信息更新答案,我将删除-1。
j_random_hacker

2

使用以下命令检查str是否具有后缀

/*
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;
}

2

通过<algorithms>反向迭代使用std :: equal算法:

std::string LogExt = ".log";
if (std::equal(LogExt.rbegin(), LogExt.rend(), filename.rbegin())) {
   
}

2
尽管此代码可以为问题提供解决方案,但最好添加有关其原因/工作方式的上下文。这可以帮助将来的用户学习并将该知识应用于他们自己的代码。在解释代码时,您还可能以投票的形式从用户那里获得正面的反馈。
borchvm

@borchvm,添加了一些解释,希望对理解有所帮助
Sergei

1

关于Grzegorz Bazior的回应。我使用了此实现,但原始实现存在错误(如果将“ ..”与“ .so”进行比较,则返回true)。我建议修改功能:

bool endsWith(const string& s, const string& suffix)
{
    return s.size() >= suffix.size() && s.rfind(suffix) == (s.size()-suffix.size());
}

1

我认为发布不使用任何库函数的原始解决方案是有道理的...

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

添加一个简单的,std::tolower我们可以使这种情况不区分大小写

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

感谢您添加。照明解决方案始终很棒
ekkis


0

如果您像我一样,但是对C ++的纯粹主义却不那么感兴趣,那么这里有一个古老的混血儿。字符串多于几个字符时,存在一些优势,因为大多数memcmp实现都尽可能比较机器单词。

您需要控制字符集。例如,如果此方法与utf-8或wchar类型一起使用,则存在一些缺点,因为它不支持字符映射-例如,当两个或多个字符在逻辑上相同时

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

0

我的两分钱:

bool endsWith(std::string str, std::string suffix)
{
   return str.find(suffix, str.size() - suffix.size()) != string::npos;
}
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.