substr() 是提取子串最直接的方法,需手动计算起始位置和长度,注意处理空字符串、偶数长度歧义、utf-8 编码及 npos 误用等边界与编码问题。

用 substr() 提取子串最直接
绝大多数情况下,substr() 就是你要的答案。它接受起始位置和长度两个参数,不支持“从末尾数”或“自动居中”这种高级语义,所以中间部分得自己算好起始点和长度。
- 如果字符串长度为奇数(比如 7),中间字符只有一个,位置是
str.length() / 2(整除,结果为 3),取 1 个字符:str.substr(3, 1) - 如果想取“中间三个字符”,长度为 7 时起始是 2,长度是 3:
str.substr(2, 3) - 注意:起始位置超出范围会抛出
std::out_of_range;长度超出剩余字符数时,substr()自动截断,不会报错
处理偶数长度时的常见歧义
“中间部分”本身不明确——长度为 8 的字符串,中间到底是第 4–5 位(两个字符),还是第 3–6 位(四个字符)?不同业务场景要自己定义清楚,代码里不能靠猜。
- 取“中心对称的两个字符”(如 "abcd" → "bc"):
str.substr(str.length()/2 - 1, 2) - 取“靠左的中间两字符”(如 "abcd" → "ab"):
str.substr(0, 2)显然不对,得先算:str.substr((str.length()-2)/2, 2) - 建议把逻辑封装成函数,比如
mid(str, n)表示取长度为n的中间子串,并在注释里写清规则(是否向下取整、越界如何处理)
避免 std::string::npos 误用
有人试图用 find() 配合 npos 定位“中间”,这是典型误解。npos 是查找失败的返回值,和位置计算无关,强行用于索引会导致极大偏移(通常是 18446744073709551615),立刻崩溃。
- 错误写法:
str.substr(str.find("x"), 2)—— 如果没找到 "x",find()返回npos,传给substr()就炸了 - 安全写法:先判断
auto pos = str.find("x"); if (pos != std::string::npos) { ... } -
npos只该出现在比较中,不该参与算术运算
Unicode 字符串不能直接用 length()
std::string::length() 返回的是字节数,不是字符数。如果字符串含 UTF-8 编码的中文或 emoji,按字节切会把一个字符切成两半,得到乱码。
- 例如
"你好"在 UTF-8 下占 6 字节,length()返回 6,但只有 2 个字符 - 真要处理 Unicode 中间部分,得先用 ICU、UTF8-CPP 或 C++20 的
std::ranges::views::utf8(实验性)转成 code point 序列,再按字符数算位置 - 日常项目若确定只跑 ASCII,就放心用
length();否则别绕开编码问题,硬切字节必出错
实际写的时候,最常被忽略的是:中间位置计算没考虑空字符串或长度不足目标子串的情况,一上来就 substr(len/2, 1),结果 len 是 0 或 1 时行为不符合预期。边界检查比算法本身还重要。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











