<p>用 substr() 截掉固定长度后缀最直接:s = s.substr(0, s.size() - n);但须先检查 if (n <= s.size()),否则无符号整数回绕会导致崩溃或静默错误。</p>

用 substr() 截掉固定长度后缀最直接
如果明确知道要删掉最后 n 个字符(比如删掉扩展名前的 4 个字符),substr() 是最轻量、最安全的方式。它不依赖内容匹配,只看长度,不会误删中间相同子串。
注意:必须先检查字符串长度,否则 substr() 在 n > size() 时会抛出 std::out_of_range 异常。
- 正确写法:
s = s.substr(0, s.size() - n); - 加保护:
if (n 或 <code>s.resize(s.size() - n); -
erase()和resize()效率略高,但语义不如substr()清晰;erase()返回引用,可链式调用
用 erase() 删除后缀时容易越界
erase() 看起来简洁,但参数是起始位置和长度,不是“从末尾删几个”——很多人错写成 s.erase(s.size() - n),这其实是删掉从该位置到结尾的所有字符,结果对;但若写成 s.erase(s.size() - n, n) 才是标准用法。问题在于:一旦 n > s.size(),s.size() - n 会变成极大正数(无符号整数回绕),导致崩溃或静默错误。
- 安全写法:
if (n - 别依赖
length()和size()差异——它们完全等价,选一个保持一致即可 - 调试时可在 erase 前加断言:
assert(n (仅 debug 模式)
按内容匹配后缀(如 ".txt")不能只靠长度
如果目标是删掉特定后缀(例如所有以 ".log" 结尾的字符串),就不能硬删固定长度——因为文件名长度不一,得先判断是否真实存在该后缀。
立即学习“C++免费学习笔记(深入)”;
- 推荐用
if (s.length() >= 4 && s.substr(s.length() - 4) == ".log") s.resize(s.length() - 4); - 更健壮:用
std::string::ends_with()(C++20),但老标准需手写逻辑 - 别用
find()或rfind()查后缀——它们返回的是首次/末次出现位置,无法保证在末尾 - 大小写敏感:".LOG" ≠ ".log",需要
std::equal配std::tolower才能忽略大小写
性能与可读性取舍:resize() 比 substr() 少一次内存分配
当原字符串很长,且只是简单截断时,resize() 直接修改内部长度,不产生新对象;substr() 返回新字符串,可能触发堆分配(尤其短字符串优化失效时)。但在绝大多数场景下,这点差异可忽略。
- 追求极致性能且确定不会越界:用
s.resize(s.size() - n); - 强调意图清晰、避免副作用:用
s = s.substr(0, s.size() - n); - 如果后续还要用原字符串(比如日志中保留备份),千万别用
resize()或erase()原地修改——它们不可逆
真正容易被忽略的,是长度检查的时机:不是“删完再判断”,而是“删之前必须判”。哪怕只差 1 字节,无符号整数溢出会让程序行为完全失控。


















