Python splitlines:末尾有换行,为什么没有多出一条空记录

前天 6阅读

导入一段手工维护的任务清单时,程序原本用换行符拆分字符串,再把列表长度当成条目数。编辑器给最后一行补上换行,数量就多了一条;换成另一台机器导出的文本后,末尾又残留回车。这里先别删除所有空字符串,应该确定业务里的“行”和“字段”分别是什么。行结束标记与一条空白记录不是同一个概念。

用最小样本看出两种拆法

下面代码只处理内存中的合成文字,不读取用户文件。保存为 demo.py 后运行 python demo.py。样本同时放入回车换行、一个真正的空行,以及 Unicode 行分隔符;打印时使用 repr,让肉眼看不到的字符显形。最后的断言检查结果含义,避免只看到程序没报错便认为处理正确。

samples = "甲\r\n\r\n乙\u2028丙\n"
print("split:", repr(samples.split("\n")))
print("lines:", repr(samples.splitlines()))
kept = samples.splitlines(keepends=True)
print("kept:", repr(kept))
print("roundtrip:", "".join(kept) == samples)
print("empty:", "".splitlines(), "".split("\n"))
print("one:", repr("甲\n".splitlines()))
print("two:", repr("甲\n\n".splitlines()))
assert samples.splitlines() == ["甲", "", "乙", "丙"]
assert "".join(kept) == samples
assert "\n".splitlines() == [""]

第一行输出保留了两个尾部回车,还把 Unicode 行分隔符留在正文内;第二行则得到甲、空字符串、乙、丙四条。第五行的对照是空列表与只含一个空字符串的列表。第六行的单个尾部换行没有额外造出空行,第七行两个连续换行却留下一个真实空行。这几种情况不能混为一谈。

是否保留结束符由后续任务决定

默认结果去掉每行的结束标记。做逐行校验时通常方便,但若要改完一段文字再原样拼接,可以先使用 keepends=True。例子把保留结束符的片段直接拼回原文并断言相等,说明这里保留的是现有字符串里的信息;它不会替你恢复读取文件时已经被转换过的换行格式。

Python splitlines:末尾有换行,为什么没有多出一条空记录

AI生成概念示意图:剪开行边界时,真正的空行与最后一行的结束标记需要分别处理。

不要先对整段文字调用 strip 再拆行。那会提前抹掉开头和结尾的空白,之后已经无法判断它们是无意空格还是有意义的空记录。更清楚的顺序是先分行、记录原始行号,再针对每行决定去空格、忽略空行或报错。忽略规则应由数据格式定义,而不是依靠一次看起来整齐的输出。

按行拆分也有适用边界

splitlines 识别的边界比常见的回车和换行更多,包含垂直制表、换页和若干 Unicode 分隔字符。处理自然文本时这可能正合适;处理协议、日志帧或固定分隔字段时,却可能把本应属于内容的字符拆开。若格式明确只允许一个分隔符,就应照格式解析,不能因方法名带有“行”而扩大规则。

空字符串代表没有任何行,单个换行代表一条空行,这种约定适合多数文本处理,但未必符合表格导入约定。若业务要求尾部分隔符必须对应一个空字段,应保留固定分隔符拆法,并显式处理回车。不要把换方法当成通用修复,要把输入样本、期望记录数及空行政策一起写进验收条件。

此外,当前例子讨论的是已经解码的 str。二进制内容应先按正确编码解码,不能假定 bytes 的分行规则覆盖相同的 Unicode 字符。大文件也不宜为了调用此方法一次性全部读入;可以按文件迭代处理,再确认读取层采用的换行策略。先分清字符解码、读取转换和业务分行三层,异常才容易定位。

资料核对日期:2026年10月2日(北京时间)。示例在 Python 3.12.14 中独立运行,具体输出以本文实测为准。

官方参考:Python str.splitlines 官方文档。

文章版权声明:除非注明,否则均为云鹊BLOG原创文章,转载或复制请以超链接形式并注明出处。