han*_*dle 6 python windows newline eol
我想通过 Windows CMD(控制台)从 Python 中使用类似 UNIX EOL (LF) 的管道文本。然而,Python 似乎会自动将单个换行符转换为 Windows 风格的行尾 (EOL)字符(即\r\n, <CR><LF>, 0D 0A, 13 10):
#!python3
#coding=utf-8
import sys
print(sys.version)
print("one\ntwo")
# run as py t.py > t.txt
Run Code Online (Sandbox Code Playgroud)
结果是
3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)]
one
two
Run Code Online (Sandbox Code Playgroud)
或十六进制... 6F 6E 65 0D 0A 74 77 6F 0D 0A
第二个 EOL 是因为
print()默认为end='\n',但它也会进行转换。
print没有newline像 for 那样的参数或属性open(),那么如何控制它呢?
请参阅此答案: https ://stackoverflow.com/a/34997357/1619432
print()通常写入到sys.stdout. 以下是非交互模式的文档摘录:
stdout 用于 print() 的输出
sys.stdout:解释器用于标准...输出的文件对象
这些流是常规文本文件,类似于 open() 函数返回的文件。
Windows 上的字符编码是 ANSI
标准流...像常规文本文件一样进行块缓冲。
注意
要向标准流写入或读取二进制数据,请使用底层二进制缓冲区对象。例如,要将字节写入 stdout,请使用 sys.stdout.buffer.write(b'abc')。
让我们先尝试一下这种直接方法:
import sys
print("one\ntwo")
sys.stdout.write('three\nfour')
sys.stdout.buffer.write(b'five\nsix')
Run Code Online (Sandbox Code Playgroud)
结果是
five\n
sixone\r\n
two\r\n
three\r\n
four
Run Code Online (Sandbox Code Playgroud)
缓冲区写入似乎按预期工作,尽管它“扰乱”了输出顺序。
在直接写入缓冲区之前进行刷新有助于:
import sys
print("one\ntwo")
sys.stdout.write('three\nfour')
sys.stdout.flush()
sys.stdout.buffer.write(b'five\nsix')
Run Code Online (Sandbox Code Playgroud)
结果是
one\r\n
two\r\n
three\r\n
fourfive\n
six
Run Code Online (Sandbox Code Playgroud)
但它仍然没有“修复” print()。回到文件对象/流/文本文件(Python 数据模型中 IO 对象的简短信息):
https://docs.python.org/3/glossary.html#term-text-file
能够读取和写入 str 对象的文件对象。通常,文本文件实际上访问面向字节的数据流并自动处理文本编码。文本文件的示例是以文本模式(“r”或“w”)打开的文件、sys.stdin、sys.stdout 和 io.StringIO 的实例。
那么(如何)可以重新配置或重新打开 sys.stdout文件来控制换行行为?到底是什么?
>>> import sys
>>> type(sys.stdout)
<class '_io.TextIOWrapper'>
Run Code Online (Sandbox Code Playgroud)
文档:类io.TextIOWrapper(缓冲区,编码=无,错误=无,换行=无,line_buffering = False,write_through = False):
换行符控制如何处理行结尾。它可以是 None、''、'\n'、'\r' 和 '\r\n'。
它的工作原理如下:
从流中读取输入时,如果换行符为 None,则启用通用换行符模式。输入中的行可以以“\n”、“\r”或“\r\n”结尾,这些行在返回给调用者之前会被转换为“\n”。
如果是 '',则启用通用换行模式,但行结尾会以未翻译的形式返回给调用者。
如果它具有任何其他合法值,则输入行仅由给定字符串终止,并且行结尾将以未翻译的形式返回给调用者。将输出写入流时,如果换行符为 None,则写入的任何 '\n' 字符都将转换为系统默认行分隔符os.linesep 。 如果换行符是 '' 或 '\n',则不会进行任何翻译。 如果换行符是任何其他合法值,则写入的任何“\n”字符都将转换为给定字符串。
让我们来看看:
>>> sys.stdout.newline = "\n"
>>>
Run Code Online (Sandbox Code Playgroud)
好的,那么呢
import sys
sys.stdout.newline = '\n'
print("one\ntwo")
Run Code Online (Sandbox Code Playgroud)
不起作用:
one\r\n
two\r\n
Run Code Online (Sandbox Code Playgroud)
因为该属性不存在:
>>> sys.stdout.newline
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: '_io.TextIOWrapper' object has no attribute 'newline'
Run Code Online (Sandbox Code Playgroud)
我应该早点检查一下..
>>> vars(sys.stdout)
{'mode': 'w'}
Run Code Online (Sandbox Code Playgroud)
所以实际上,我们没有任何newline属性需要重新定义。
有什么好用的方法吗?
>>> dir(sys.stdout)
['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__',
'__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__',
'__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__',
'__init__', '__init_subclass__', '__iter__', '__le__', '__lt__',
'__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable',
'_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding',
'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode',
'name', 'newlines', 'read', 'readable', 'readline', 'readlines',
'seek', 'seekable', 'tell', 'truncate', 'writable', 'write',
'writelines']
Run Code Online (Sandbox Code Playgroud)
并不真地。
但我们至少可以替换缓冲区末端的默认接口,指定所需的换行符:
import sys, io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, newline='\n' )
print("one\ntwo")
Run Code Online (Sandbox Code Playgroud)
最终结果是
one\n
two\n
Run Code Online (Sandbox Code Playgroud)
要恢复,只需重新分配sys.stdout到您制作的副本即可。或者,显然不建议使用内部保存sys.__stdout__来做到这一点。
警告:请参阅下面eryksun的评论,这需要小心。使用他的解决方案(下面的链接):
似乎也可以重新打开文件,请参阅使用 io.TextIOWrapper 包装打开的流以获取灵感,并通过此答案/sf/answers/2449815021/了解实现。
如果您想仔细查看,请查看 Python (CPython) 源代码: https://github.com/python/cpython/blob/master/Modules/_io/textio.c
还有os.linesep,让我们看看它是否真的是 Windows 的“\r\n”:
>>> import os
>>> os.linesep
'\r\n'
>>> ",".join([f'0x{ord(c):X}' for c in os.linesep])
'0xD,0xA'
Run Code Online (Sandbox Code Playgroud)
这可以重新定义吗?
#!python3
#coding=utf-8
import sys, os
saved = os.linesep
os.linesep = '\n'
print(os.linesep)
print("one\ntwo")
os.linesep = saved
Run Code Online (Sandbox Code Playgroud)
它可以在交互模式下,但显然不能在其他模式下:
\r\n
\r\n
one\r\n
two\r\n
Run Code Online (Sandbox Code Playgroud)