判断字符串是否以某后缀结尾应先检查长度再比较,推荐用C++17的ends_with或rfind;删除所有匹配后缀需while循环配合erase,注意空后缀和边界情况。

如何判断字符串是否以某后缀结尾
直接用 std::string::substr 容易越界,尤其当后缀长度大于原字符串时。更安全的做法是先比较长度,再用 std::string::compare 或 C++17 的 std::string::ends_with(需编译器支持 C++20 也可用 std::string_view 模拟)。
常见错误:写成 s.substr(s.size() - suffix.size()) == suffix,没检查 s.size() ,触发 <code>std::out_of_range 异常。
- 推荐写法(C++11 兼容):
if (s.size() >= suffix.size() && s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0)
- 若已知后缀非空且长度可控,也可用
s.rfind(suffix) == s.size() - suffix.size()——rfind返回std::string::npos时不会越界
删除一次后缀 vs 删除所有匹配后缀
“删除所有特定后缀”不是指删掉末尾重复出现的多个后缀(比如 "ababab" 中删所有 "ab"),而是反复删直到不再以该后缀结尾——即贪心地、从右往左逐次剥离。
容易混淆的点:有人误以为 erase + rfind 一次就够了,但像 "test.log.log.log" 删 ".log" 应得 "test",而非 "test.log.log"。
立即学习“C++免费学习笔记(深入)”;
- 正确逻辑:用 while 循环,每次检查是否以 suffix 结尾,是则
erase末尾部分 - 注意
erase参数:用s.erase(s.size() - suffix.size()),不是erase(pos, len)—— 后者要算起始位置,易错 - 避免无限循环:确保每次成功删除后字符串变短,且条件判断放在循环开头(先 check 再 erase)
封装成可复用函数的注意事项
如果写成函数,参数传递方式影响性能和语义:std::string 值传入会拷贝,const std::string& 更高效;但若函数要修改原串,就得用 std::string&。
另一个坑:返回值类型。返回 void 修改原串最直观;若返回新字符串,别忘了处理空 suffix —— 空后缀会导致无限循环或未定义行为。
- 安全签名示例:
void remove_suffix(std::string& s, const std::string& suffix) { ... } - 必须加判空:
if (suffix.empty()) return;,否则s.size() - 0没问题,但逻辑上无意义,且可能掩盖调用方错误 - 若 suffix 含 null 字符(
'\0'),std::string能正常存,但某些旧代码误用c_str()截断,导致匹配失败
性能与边界场景验证
极端情况容易漏测:空字符串、suffix 长度为 0、suffix 比 s 还长、suffix 等于 s 本身(删完应得空串)、连续多次匹配(如 "a.a.a" 删 ".a")。
实际中,如果对大量短字符串高频调用,可考虑用 std::string_view 避免临时构造 string 对象,但要注意 view 生命周期不能短于函数作用域。
- 测试用例建议覆盖:
"","abc","abc.def","abc.def.ghi","abc.def.ghi."(结尾是点,不匹配) - 不要依赖
std::string::pop_back()替代 erase —— 它只能删一个字符,不适用于任意长度 suffix - 多线程环境下,若多个线程共享同一 string 并调用该函数,必须加锁 —— 标准库 string 非线程安全
真正麻烦的是 suffix 本身含特殊含义字符(比如正则符号),但这里纯字面匹配,所以不用转义。只要记住:长度检查永远在前,循环删除要防死锁,空输入永远先拦住。


















