c++oding="utf-8" ?>
最直接解法是用std::regex_replace匹配"-+"替换为"-",但需注意libstdc++旧版支持差、msvc更可靠;高频场景应复用regex对象;若需保留首尾连字符或处理unicode,须额外预处理或改用双指针法。

用 std::regex_replace 一次性替换连续连字符为单个
最直接的解法是正则匹配一个或多个连字符,替换成单个。但要注意:C++11 的 std::regex 在部分标准库实现(如 libstdc++ 旧版本)中 regex 支持不完整或性能差,MSVC 和较新 libc++ 通常更可靠。
示例代码:
std::string s = "a---b----c";
s = std::regex_replace(s, std::regex("-+"), "-");
// 结果: "a-b-c"
- 匹配模式
"-+"表示“一个及以上连字符”,比"-{2,}"更简洁,也能处理单个连字符(即不误删合法单连字符) - 若字符串里有大量连字符或高频调用,regex 构造开销明显;可将
std::regex对象提取为静态局部变量复用 - 注意:
std::regex_replace不修改原串,必须赋值回变量
手写双指针遍历,零依赖且可控性强
当需要避免 regex、或对性能/内存严格控制(如嵌入式、高频处理),推荐双指针原地去重。核心逻辑是:只保留第一个连字符,跳过后续紧邻的。
关键判断点:if (s[i] == '-' && i > 0 && s[i-1] == '-') —— 这个条件容易漏掉边界检查,导致越界访问。
- 必须确保
i > 0再访问s[i-1],否则s[-1]是未定义行为 - 写入位置
write初始为 0,每次决定保留字符时才写入并递增;最后用s.resize(write)截断 - 该方法不会创建临时字符串,空间复杂度 O(1),适合超长字符串
区分“多余”含义:是否要保留开头/结尾的连字符?
题目说“多余连字符”,但实际业务中常需进一步定义:“首尾单个连字符是否合法?”比如路径 "--help" 或文件名 "log-.txt" 可能需保留边界连字符,仅压缩中间的。
- 若需保留首尾,不能简单全局替换;得先跳过开头连字符段,再处理中间,最后跳过结尾段
- 可用
s.find_first_not_of('-')和s.find_last_not_of('-')定位有效区间,再对中间子串做双指针压缩 - 错误做法:直接对整个字符串执行
regex_replace,会把"--abc--"变成"-abc-",但若业务要求首尾只能最多一个,则结果正确;若要求完全去掉首尾,则需额外erase前后
Windows 路径场景下慎用:连字符和短横线不是一回事
在文件路径或命令行参数中,用户可能混用 ASCII 连字符 '-'(U+002D)和 Unicode 短横线(如 U+2010、U+2011)。std::regex 和普通字符比较默认只识别前者。
- 如果输入含 Unicode 短横线,上述所有方案都会忽略它们,导致“删不干净”
- 此时需先 normalize 字符串(如转为 NFC 形式),或用支持 Unicode 的正则库(如 ICU、Boost.Regex)并启用
std::regex_constants::icase | std::regex_constants::collate等标志 - 更稳妥的做法是预处理:用
std::wstring_convert(C++17 已弃用)或 C++20std::from_chars+ UTF-8 解码,再统一映射为 ASCII 连字符
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











