最可靠方法是用 std::stringstream 配合 std::getline 逐行读取,再用 std::all_of 或 trim 判断每行是否全为空白字符,仅跳过整行为空白的行,保留原始格式与换行符兼容性,C++11 起可用。

用 std::getline 逐行读取并跳过空行最可靠
直接对整个字符串做正则或 erase 删除换行符容易误伤内容(比如中间有多个连续换行,或行首尾有空格的“伪空行”)。真实场景中,“空行”指的是只含换行符(\n、\r\n)或仅含空白字符(空格、制表符)的行。所以必须按行切分再判断。
推荐做法:用 std::stringstream 包裹原字符串,配合 std::getline 逐行提取,对每行用 std::all_of 判断是否全为空白:
std::string remove_blank_lines(const std::string& s) {
std::stringstream ss(s);
std::string line, result;
while (std::getline(ss, line)) {
// 检查是否为空行:去除前后空白后长度为 0
auto trimmed = line;
trimmed.erase(trimmed.begin(), std::find_if_not(trimmed.begin(), trimmed.end(), ::isspace));
trimmed.erase(std::find_if_not(trimmed.rbegin(), trimmed.rend(), ::isspace).base(), trimmed.end());
if (!trimmed.empty()) {
if (!result.empty()) result += "\n";
result += line; // 保留原始行内空白和末尾换行符(<code>getline</code> 已剥离)
}
}
return result;
}
-
std::getline自动处理\n和\r\n,无需手动适配换行符差异 - 保留原行的内部空格和制表符,只过滤整行为空白的行
- 不依赖 C++20 的
std::ranges或第三方库,C++11 起可用
用 std::regex_replace 简单粗暴但有陷阱
如果确定输入格式干净(比如只有 \n 换行、无前导/尾随空格),可以用正则一次替换。但要注意:C++ 标准库正则对换行符支持弱,^ 和 $ 默认不匹配行首/行尾(需加 std::regex_constants::extended 且仍不可靠)。
更稳妥的写法是匹配“换行符 + 可选空白 + 换行符”模式:
立即学习“C++免费学习笔记(深入)”;
std::string s = "a\n\n \n b\n\n";
s = std::regex_replace(s, std::regex("\n[[:space:]]*\n"), "\n");
// 注意:这只能处理两个换行之间的空白行,开头结尾的空行需额外处理
- 无法处理 Windows 的
\r\n混合情况,除非显式写成\r?\n[[:space:]]*\r?\n - 多次运行可能因重叠匹配出错(如三个连续换行会被两次替换)
- 性能比循环
getline差,尤其大字符串时
注意 \r\n 和字符串字面量的换行符一致性
从文件读入的字符串可能含 \r\n,而代码里写的 "a\n\nb" 是 \n。若混用,std::getline 仍能正确分割(它把 \r\n 当作一个换行),但手动用 find('\n') 就会漏掉 \r 后的内容。
- 永远优先用
std::getline,别自己解析换行符 - 若必须用
find,检查\r\n和\n两种情况:pos = s.find_first_of("\r\n") - 从
std::ifstream读取时,确保以文本模式打开(默认就是),避免二进制模式下\r\n被转义
别忽略“空行”的定义差异
业务中“空行”可能指:① 完全无字符(只有换行符);② 仅含空格/制表符;③ 含注释(如 // 开头)也视为空行。上面示例只覆盖①②。若需③,得在 trimmed.empty() 前加判断:
if (line.find("//") == 0 || line.find("/*") == 0) continue;
这种逻辑一多,就该抽成独立函数,而不是堆在循环里。真正难的不是删空行,是明确“谁算空行”——这个定义一旦模糊,所有实现都会偏移。


















