前置信息
关于换行符,一共有下面这几种:
| 字符 |
描述 |
ASCII 16进制 |
\n |
换行符 |
0x0A |
\r |
回车符 |
0x0D |
\r\n |
回车换行符 |
0x0D 0x0A |
上面每一个换行符,都能够让文本正确换行。Linux 常用的换行符是 \n,而 Windows 默认的换行符是 \r\n,Mac 的换行符是 \r。
问题
使用如下代码,读取剪贴板,按行分割,去除每一行的换行符,然后再按行写入文本文件,换行方式使用 os.linesep,结果发现,写入的文本,每行之间又额外加了一个空行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import os
from pyperclip import paste from rich.console import Console
console = Console()
def print_hex(text: str): """ 智能打印文本的十六进制表示 高亮显示回车符(0d)和换行符(0a) """ hex = text.encode('utf-8').hex() format_hex = hex.replace('0d', '[red]0d[/]').replace('0a', '[red]0a[/]') console.print(f"[yellow]{text} -> {format_hex}")
def remove_cr_lf(text: str) -> str: """ 删除末尾的换行符 """ return text[:].rstrip().rstrip('\r').rstrip('\n').rstrip('\r\n')
def split_lines(text: str) -> list[str]: """ 分割文本 """ lines = [remove_cr_lf(i) for i in text.split("\n")] console.print(f'[green]分割了 {len(lines)} 行[/green]') return lines
console.input("请复制所有的内容,复制后按下回车按键读取: ") content = paste()
lines: list[str] = split_lines(content)
with open('./bug.txt', 'w', encoding='utf-8') as f: for line in lines: print_hex(line) f.write(line + os.linesep)
|
复制的文本是:
运行结果:


可以看到,line 变量的结尾确实是没有换行符的。但是,写入的txt每次都是换了两行。使用 winhex 打开查看:

所有的换行符都变成了 0d 0d 0a。
把 print_hex(line) 更换成 print_hex(line + os.linesep),下面每一个 16 进制都是即将写入到输出文件中的。

可以看到,打印的箭头之前是有换行符的,这是复制的原文本自带的换行符。然后被函数统一成了 CRLF 也就是 0d 0a。
那么尝试直接在终端打印 print(os.linesep.join(lines)),也就是把整个准备输出到文件中的内容打印出来。

结果是正常的。
打印 16 进制 print_hex(os.linesep.join(lines)),也就是计算整个将要写入到文件中的内容所对应的 16 进制。

也是正常的!0d 每次只出现过一次。也就是说,在二进制层面,写入的文件应当是完全正确的 0d 0a 换行符。
那么尝试直接写入这个字符串
1 2 3 4 5 6 7 8
| teset = os.linesep.join(lines)
print(teset) print_hex(teset)
with open('./bug.txt', 'w', encoding='utf-8') as f: f.write(teset)
|
依然不行!还是多个换行符 0d 0d 0a

winhex 查看,依然是写入了两次 0d
解决方法 1
在 Python 中写入文件时出现两次换行符(0D 0D 0A)的问题,主要是由于文本模式(’w’)和行结束符转换机制共同作用导致的。
在 Windows 系统中,Python 文本模式默认会将 \n(0A)转换为 \r\n(0D 0A)
如果输入字符串 teset 中已经包含 \r\n(0D 0A),Python 会将其中的 \n(0A)再次转换为 \r\n(0D 0A),最终导致 \r\r\n(0D 0D 0A)。
最直接的解决方法是使用二进制模式(wb)写入文件,避免 Python 进行行结束符转换:
1 2 3 4 5 6 7
| teset = os.linesep.join(lines)
print(teset) print_hex(teset)
with open('./bug.txt', 'wb') as f: f.write(teset.encode('utf-8'))
|

解决方法 2
指定 newline='' 参数:
1 2
| with open('./bug.txt', 'w', encoding='utf-8', newline='') as f: f.write(teset)
|