std::transform转大小写时需将char先转unsigned char再调用std::toupper/tolower,否则有符号扩展导致UB;trim应自定义基于std::isspace的逻辑;分割字符串推荐find+substr而非stringstream;性能敏感场景优先用std::string_view。

std::transform 转大小写时,别直接传 toupper/tolower
常见错误是写 std::transform(s.begin(), s.end(), s.begin(), ::toupper),结果在某些平台(尤其是 Linux + glibc)下遇到 std::bad_cast 或乱码。原因是 toupper 只接受 int 且要求参数在 unsigned char 范围内,而 char 在某些编译器里默认是有符号的,负值传进去就 UB。
正确做法是先转成 unsigned char 再调用:
std::transform(s.begin(), s.end(), s.begin(),
[](unsigned char c) { return std::toupper(c); });- Windows MSVC 通常不报错,容易掩盖问题;Linux/macOS 更严格,务必测试
- 如果处理 Unicode(如 UTF-8),
std::toupper完全不适用,得用 ICU 或std::locale配合 facet(但开销大) - 小写同理,用
std::tolower+ 同样转换
去除首尾空格:自己写比依赖 Boost 更可控
boost::trim 看起来省事,但引入整个 Boost 太重,而且对非 ASCII 空白字符(比如 \u3000 全角空格)默认不识别。C++20 前没标准 trim,自己写几行更靠谱。
关键点在于:空格不止是 ' ',还有 '\t'、'\n'、'\r'、'\f'、'\v' —— 这些才是 std::isspace 认的“空白”。
立即学习“C++免费学习笔记(深入)”;
auto is_ws = [](unsigned char c) { return std::isspace(c); };
s.erase(s.begin(), std::find_if_not(s.begin(), s.end(), is_ws));
s.erase(std::find_if_not(s.rbegin(), s.rend(), is_ws).base(), s.end());- 必须用
unsigned char传给std::isspace,否则和toupper一样有符号扩展风险 - 不要用
std::string::find_first_not_of(" \t\n\r\f\v"),它硬编码字符集,无法适配 locale - 如果需支持 Unicode 空白(如中文空格),得换正则或专用库,别硬扩
按分隔符分割字符串:别手写 while + find,用 std::stringstream 有陷阱
std::stringstream 遇到连续分隔符(如 "a,,b")会跳过空字段,得到 {"a", "b"} 而不是预期的 {"a", "", "b"}。这不是 bug,是设计如此 —— 它按“单词”切,不是按分隔符切。
真要精确控制,用 std::string::find + substr 最稳:
std::vector<std::string> split(const std::string& s, char delim) {
std::vector<std::string> out;
size_t start = 0, end = 0;
while ((end = s.find(delim, start)) != std::string::npos) {
out.push_back(s.substr(start, end - start));
start = end + 1;
}
out.push_back(s.substr(start));
return out;
}- 这个版本保留空字段,比如
split("a,,b", ',')→{"a", "", "b"} - 如果分隔符是字符串(如
"::"),不能用find单字符版,得用find(delim)并调整start步长 - C++23 的
std::ranges::split_view更简洁,但还没普及,线上项目慎用
性能敏感场景:避免反复构造 std::string 临时对象
比如循环里写 s.substr(i, len).c_str(),每次调用都分配内存、拷贝内容。在日志、解析、高频字符串处理中,这会成为瓶颈。
能用 std::string_view 就别用 std::string:
// 好:只存指针+长度,零拷贝 std::vector<std::string_view> views; views.emplace_back(s.data() + i, len); <p>// 坏:隐式构造 string,堆分配 std::vector<std::string> copies; copies.emplace_back(s.substr(i, len));
-
std::string_view不拥有数据,确保原字符串生命周期长于 view - 函数参数优先用
std::string_view(C++17 起),避免重载const char*和std::string两套 - 调用 C 接口(如
printf、open)时,仍需.data()+.size()配合,不能直接传string_view
字符串操作里最常被忽略的,其实是字符类型的符号性 —— 一个 char 是 signed 还是 unsigned,能决定 toupper、isspace 是否崩溃。这点在跨平台代码里,比语法糖重要得多。











