substr是std::string成员函数,按字节截取子串:pos越界抛异常,len超长自动截断;UTF-8中文易乱码;推荐find定位后substr,大字符串优先用string_view避免拷贝。

substr 函数的基本用法和边界陷阱
substr 是 std::string 的成员函数,不是全局函数,调用前必须确保对象非空且索引合法。它有两个重载形式:substr(size_t pos) 和 substr(size_t pos, size_t len)。第一个参数 pos 是起始位置(从 0 开始),第二个是截取长度 —— 不是“结束位置”,这点最容易错。
- 如果
pos == str.length(),返回空字符串"";这是合法的,不会崩溃 - 如果
pos > str.length(),抛出std::out_of_range异常(Debug 模式下常见,Release 可能静默 UB) -
len超出剩余字符数时,自动截断到末尾,不会报错 —— 这是设计行为,不是 bug - 别写
s.substr(2, 5)期望拿到第 2 到第 5 位:实际是“从下标 2 开始取 5 个字符”,即 [2, 6) 区间
中文、UTF-8 字符串用 substr 会乱码吗?
会,而且大概率乱码。substr 按字节操作,不识别 UTF-8 编码单元。一个汉字在 UTF-8 中占 3 字节,若 pos 落在某个汉字中间(比如偏移量为 1 或 4),截出来的就是非法 UTF-8 字节序列,后续 std::cout 或 JSON 库可能直接丢弃或显示 。
- 纯 ASCII 场景(英文、数字、基本符号)完全安全
- 含中文时,必须先用 ICU、utf8cpp 或手写逻辑将字符串转成
std::vector<:string></:string>(每个元素是一个完整 Unicode 码点),再按“字符数”而非“字节数”计算pos和len - 没有“万能安全 substr”,C++ 标准库不提供 Unicode 支持
替代方案:用 find + substr 提取子串更可靠
直接硬算下标容易出错,尤其当目标子串位置不确定时。更健壮的做法是先用 find 定位起始/结束标记,再传给 substr。
std::string s = "name=alice&age=30&city=shanghai";
size_t start = s.find("age="); // 返回 9
if (start != std::string::npos) {
start += 4; // 跳过 "age="
size_t end = s.find('&', start);
std::string age_str = (end == std::string::npos)
? s.substr(start)
: s.substr(start, end - start); // 得到 "30"
}-
find返回std::string::npos表示未找到,必须检查,否则传给substr会触发异常 - 用
find_first_of或find_last_of可处理多分隔符场景(如空格、逗号、换行混用) - 避免多次
substr链式调用(如s.substr(a).substr(b)),每次都会分配新字符串,性能差
substr 性能与移动语义的关系
substr 总是返回新分配的 std::string,即使原字符串很大,它也不会共享内存或延迟拷贝。C++11 后虽支持移动语义,但 substr 本身不返回右值引用,无法触发移动优化。
立即学习“C++免费学习笔记(深入)”;
- 对大字符串频繁截取(如日志解析),考虑改用
std::string_view(C++17)避免拷贝:std::string_view sv = std::string_view(s).substr(pos, len) -
string_view不拥有数据,使用时必须确保原字符串生命周期长于 view - 旧标准(C++11/14)无
string_view,只能用指针+长度手动管理,风险更高
实际用 substr 时,最麻烦的从来不是语法,而是得同时盯住三件事:索引是否越界、编码是否被拆开、内存是否被反复拷贝。漏掉任意一个,问题就藏在看似正常的输出里。










