substr的第一个参数是起始索引(从0开始,越界抛异常),第二个参数是最多复制的字符数(非结束下标,超长自动截断);负数索引需手动转为正向等效位置。

substr 的两个参数到底是什么意思
很多人以为 substr 是“起始位置 + 长度”,但容易忽略第二个参数是「最多取多少个字符」,不是「截到哪个下标」。它不会自动停在字符串末尾之外——如果越界,C++11 起会自动截断到 size(),不抛异常(但传负数或极大值可能触发未定义行为)。
常见错误:写成 s.substr(3, 10) 以为能安全取“第3位之后的10个”,结果 s 只有5个字符,实际拿到的是 s[3]..s[4] 共2个——这没错,但若逻辑依赖“一定拿到10个”,就会出错。
- 第一个参数
pos:从 0 开始的索引,超出size()时抛std::out_of_range - 第二个参数
count:要复制的字符数,std::string::npos表示“到末尾” - 推荐写法:
s.substr(pos, std::min(count, s.size() - pos))(手动保底),或直接用s.substr(pos)省得算
用 substr 实现类似 Python 的负数切片
C++ 标准库不支持负数下标,但你可以快速补一个等效逻辑:把负的起始位置转成正的。比如 s[-3:] 在 Python 中取后3个字符,在 C++ 里就是 s.substr(s.size() >= 3 ? s.size() - 3 : 0)。
注意边界:当 s.size() 时,s.size() - 3 是很大的无符号数(size_t),直接传给 substr 会越界崩溃。必须先判断。
立即学习“C++免费学习笔记(深入)”;
- 安全取后
n个:n - 安全取从倒数第
a个到倒数第b个(类似s[-a:-b]):先转正下标,再检查是否合法,最后调substr - 别用
static_cast去减——(s.size()) size()是无符号类型,转 int 可能溢出
substr 性能问题:它真的只是视图吗
不是。substr 返回的是新 std::string,会分配内存、拷贝字符。它不是像 std::string_view 那样的零开销视图。
如果你只是临时查看某段内容、不修改也不长期持有,用 std::string_view(s.data() + pos, len) 更高效,尤其对大字符串或高频调用场景。
- 需要修改子串?必须用
substr(string_view不可写) - 只读且生命周期不超过原字符串?优先
string_view - 用
substr后又马上传给函数?考虑函数是否接受string_view,避免多余拷贝
替代方案:C++20 的 std::string_view::substr
C++20 给 std::string_view 加了同名成员函数,行为一致但开销为零——它只调整指针和长度,不分配内存。
但注意:它的参数检查更松,pos > size() 仍抛异常;而 count 超限时自动截断,这点和 std::string::substr 一致。
- 已有
std::string s?可先转视图:std::string_view sv{s},再调sv.substr(...) - 函数参数尽量声明为
std::string_view,调用方传string或字面量都兼容 - 别在
string_view生命周期外使用其substr结果——它不管理内存
真正麻烦的不是怎么写 substr,而是忘记它每次都在堆上新建字符串,或者误以为它能像 Python 那样优雅处理负索引——这些地方一疏忽,调试时就变成找“为什么这里慢”或“为什么越界没报错”。










