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

359

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

22个回答

255

使用std::string::compare比较最后n个字符即可:

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

4
我总是讨厌计算子字符串的索引,容易出现偏移错误...我宁愿从两个字符串的末尾开始向后迭代,试图找到不匹配之处。 - xtofl
36
@Noldorin 我不同意。这很简单明了——最好的方法是使用一个库。可惜 C++ 标准库提供得有用的功能太少了。 - masterxilo
3
你建议使用哪个库来解决这个问题,这个库相比于基本上只有一行代码的函数而言有什么优势? - Brandin
71
因为这是一项非常基本的功能。C++强制我们反复重新编写相同的功能,而这些功能在其他现代计算机语言中都是开箱即用的。 人们需要去stackoverflow解决这个问题的事实表明存在问题。 - Conchylicultor
2
C++ 应该真正关注 Python,使琐碎的事情变得更容易!它可以轻松处理困难的事情,但却让简单的事情变得困难。 - user997112
显示剩余8条评论

217

使用这个函数:

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
3
我确定他们现在已经修复了他们的大漏洞;-) - Deduplicator

177

使用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));

168

请注意,从c++20开始,std::string最终将提供starts_withends_with。如果您不是从遥远的未来阅读此信息,那么您可以在C++17中使用这些startsWith/endsWith函数。似乎有机会在c++30中,c++中的字符串可能最终变得更加易用。

#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

如果你被困在使用较旧的C++,可以使用以下内容:

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

以及一些额外的辅助重载:

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

在我看来,C++ 字符串显然是有缺陷的,并且并不适合在实际的代码中使用。但至少还有希望会变得更好。


3
由于str.compare不返回布尔值,因此使用非(“!”)运算符测试“==0”并不明智,因为这可能会让读者感到困惑。请使用“... && str.compare(...) == 0”以增加清晰度。 - Thomas Tempelmann
8
当然有!如果你只需要知道一个字符串是否以某个内容开头,为什么还要搜索整个字符串呢?换句话说,你可能会搜索一段100MB长的字符串来找到末尾的那一小部分,但最终忽略了这个结果,因为它不是字符串的开头。 - Pavel P
35
C++30 预测增加了 "1"。 - Super-intelligent Shade
2
如果您接受C++17的std::string_view,它更加通用,而且您将不再需要那些变量来提高效率。 - Deduplicator
@Deduplicator 已添加基于 string_view 的版本。在我看来,如果想要使用这些函数的代码已经有了 std::string 参数,那么最好直接使用 std::string 版本。 - Pavel P
显示剩余2条评论

49

我知道问题是关于C++的,但如果有人需要一个好老式的C函数来做这个:


/*  当且仅当str以suffix结尾时返回1 */
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 ); }

6
如果您期望获取一个字符串但得到了NULL,那就是一个错误。因此,我会使用 assert() 或者崩溃来处理,而不是默默地在损坏状态下继续执行。 - Deduplicator

27

当需要从两个字符串的末尾开始迭代时,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
我以前从未注意过std::mismatch(),我想知道在算法头文件中还有什么其他内容我从未查看过... - j_random_hacker
3
我认为这值得在 Stack Overflow 上提出问题:你是否曾经浏览过可用的 STL 函数? - xtofl
6
请注意,这与std::equal具有相同的要求:您需要事先检查所假定的后缀是否不比您正在搜索它的字符串更长。忽略该检查将导致未定义的行为。 - Rob Kennedy
@RobKennedy 可以通过使用四个插座的 std::mismatch() 来解决这个问题。 - Deduplicator

18

依我之见,最简单的 C++ 解法如下:

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

警告:如果匹配失败,这将在放弃之前向后搜索整个字符串,因此可能会浪费大量的循环。


13
这样做速度相对较慢,因为你将搜索整个字符串s而不是仅测试其结尾! - Alexis Wilke
2
@nodakai,如果我有一个1Mb的字符串,它将会比纳秒多得多。 - Alexis Wilke
我不这么认为……它需要在任何情况下都执行strlen函数,然后从末尾开始查找。 - LtWorf
2
如果匹配失败,它会在放弃之前向后搜索整个字符串。它需要从候选索引开始进行正向搜索:ssize_t maybe_index = s.size()-suffix.size(); return maybe_index > 0 && (s.find(suffix, maybe_index) == maybe_index); - ncoghlan
4
std::string::size() 是一个常数时间的操作,它不需要 strlen - Thomas

12

假设字符串a和你要查找的字符串b,使用a.substr获取a的最后n个字符并将其与b进行比较(其中n是b的长度)

或者使用std :: equal(包括)

例如:

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
@Dario:你使用std::equal()的解决方案很好,使用substr()的解决方案不太好——除非你正在使用COW字符串(我相信很少有人这样做),substr()意味着创建字符串的一部分的第二个副本,这意味着涉及动态内存分配。这可能会失败,并且在任何情况下都意味着使用比其他解决方案更多的内存(而且它几乎肯定比其他解决方案慢)。 - j_random_hacker

9

使用来自<algorithms>的std::equal算法进行反向迭代:

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

从C++20开始引入了ends_with函数。


2
虽然这段代码可能提供了问题的解决方案,但最好添加上为什么/如何运作的上下文。这可以帮助未来的用户学习,并将这些知识应用到他们自己的代码中。当代码被解释时,您还可能会得到用户的积极反馈,例如点赞。 - borchvm
@borchvm,添加了一些解释,希望能帮助理解。 - Sergei Krivonos
1
如果LogExt.length()大于filename.length()会怎么样?这将读取超出filename开头的内容,可能导致段错误。看起来像是一个bug。 - Some Guy
1
如果LogExt.length()大于filename.length(),std::equal以这种方式返回false也是可以的。请参考https://en.cppreference.com/w/cpp/algorithm/equal。 - Sergei Krivonos
1
@SomeGuy 我相信你是对的。std::equal的3个迭代器形式无法推断出filename.rend(),并假设filename的长度大于LogExt。更安全的做法是使用std::equal的4个迭代器形式(需要c++14)。https://stackoverflow.com/questions/21680251/is-it-safe-to-call-stdequal-on-potentially-shorter-input-if-i-know-there-will - undefined

8

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

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

除非您使用<ctype.h>而不是<cctype>(或使用using),并包含tolower(),否则可能无法编译。此外,普通的char可能是有符号的,从而导致未定义行为。 - Deduplicator
@Deduplicator,感谢您的评论。我已经修正了我的回答。但是我不明白您关于UB的观点,请您详细说明一下? - PolarBear
只需查看一些文档。简而言之,在将字符传递给tolower()之前,您必须将其转换为“unsigned char”。最简单的方法是将lambda表达式的参数类型更改为“unsigned char”。 - Deduplicator
@Deduplicator,非常感谢您指出这个问题。我已经修复了它。 - PolarBear

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接