std::string::find_first_not_of和find_last_not_of可安全定位非空白字符起止位置;需检查first与last均不为npos才截取子串,且仅支持ASCII空白字符。

std::string::find_first_not_of 和 find_last_not_of 是最直接的组合
这两个成员函数专为定位非空白字符设计,比手写循环更安全、更符合 C++ 习惯。它们默认把空格、制表符、换行等都当作分隔符处理,但要注意:它们不识别 Unicode 空白(如 ),只处理 ASCII 范围内的空白字符。
实操时别直接修改原字符串,先用两个索引定位有效起止位置:
-
size_t first = s.find_first_not_of(" \t\n\r\f\v");—— 找第一个非空白字符位置,找不到返回std::string::npos -
size_t last = s.find_last_not_of(" \t\n\r\f\v");—— 找最后一个非空白字符位置 - 只有当
first != std::string::npos且last != std::string::npos且first 时才截取:<code>s.substr(first, last - first + 1)
自己写 trim 函数时必须处理 npos 边界情况
常见错误是忽略空字符串或全空白字符串场景,导致 s.substr(first, ...) 抛出 std::out_of_range 异常。比如 "" 或 "\t\t" 传入后,find_first_not_of 返回 npos,而 substr(npos, ...) 是未定义行为。
稳妥写法是显式判断:
立即学习“C++免费学习笔记(深入)”;
std::string trim(const std::string& s) {
size_t first = s.find_first_not_of(" \t\n\r\f\v");
if (first == std::string::npos) return ""; // 全空白或空串
size_t last = s.find_last_not_of(" \t\n\r\f\v");
return s.substr(first, last - first + 1);
}in-place trim(原地修剪)要避免 substr + 赋值带来的额外拷贝
如果确定要修改原字符串且性能敏感(比如处理大量短字符串),可以用 erase 分两步删前后:
- 先删末尾:
s.erase(s.find_last_not_of(" \t\n\r\f\v") + 1); - 再删开头:
s.erase(0, s.find_first_not_of(" \t\n\r\f\v")); - 注意顺序不能反——先删末尾再删开头,否则第二次
find_first_not_of的结果可能因长度变化而失效 - 同样要检查
npos,否则erase(npos + 1)会越界
第三方库如 Boost 里 trim 是线程安全但引入依赖
boost::algorithm::trim 内部做了和上面类似的逻辑,还支持就地(trim_in_place)和宽字符版本,但它要求链接 Boost 库。如果你项目已用 Boost,直接调用更省心;否则为一个 trim 引入整个 Boost,编译时间和二进制体积代价不小。
另外注意:boost::algorithm::trim 默认只认 ASCII 空白,和标准库行为一致;它不自动处理 UTF-8 编码的 Unicode 空白字符(如 U+2000–U+200F),这点容易被误以为“没生效”。
真正需要 Unicode 感知 trim 的场景,得用 ICU 或手动解析 UTF-8 字节序列——那已经不是简单字符串操作了。


















