阻止Python print()在Windows上自动换行转换为CRLF

时间:2018-04-07 16:08:32

标签: python windows newline eol

我想通过Windows CMD(控制台)从Python管道带有类似unix的EOL(LF)文本。但是,Python似乎会自动将单个换行符转换为Windows样式的end-of-line (EOL)字符(即\r\n<CR><LF>0D 0A13 10):

#!python3
#coding=utf-8
import sys
print(sys.version)
print("one\ntwo")
# run as py t.py > t.txt

结果

3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)]
one
two

或以十六进制... 6F 6E 65 0D 0A 74 77 6F 0D 0A

第二个EOL是因为 print()默认为end='\n',但也会进行转换。

没有newline参数或属性可供打印,就像open()一样,那么如何控制呢?

1 个答案:

答案 0 :(得分:1)

请参阅此回答: 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')

结果

five\n
sixone\r\n
two\r\n
three\r\n
four

缓冲区写入似乎可以正常工作,尽管它会“混乱”输出顺序。

直接写入缓冲区之前刷新有助于:

import sys
print("one\ntwo")
sys.stdout.write('three\nfour')
sys.stdout.flush()
sys.stdout.buffer.write(b'five\nsix')

结果

one\r\n
two\r\n
three\r\n
fourfive\n
six

但它仍然没有“修复”print()。返回文件对象/流/文本文件(IO objects in Python Data model上的简短信息):

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'>

文档:class io.TextIOWrapper(buffer, encoding=None, errors=None, newline=None, line_buffering=False, write_through=False)

  

换行符控制行结尾的处理方式。它可以是None,'',   '\ n','\ r'和'\ r \ n'。
  它的工作原理如下:
  从流中读取输入时,如果换行为“无”,则启用通用换行模式。   输入中的行可以以'\ n','\ r'或'\ r \ n'结尾,并且在返回给调用者之前将这些行转换为'\ n'。
  如果是'',则启用通用换行模式,但行结尾将返回给调用者未翻译   如果它具有任何其他合法值,则输入行仅由给定字符串终止,并且行结尾将返回给未调用的调用者。

     

写入输出到流时,如果换行为无,则写入的任何'\ n'字符都会转换为系统默认行分隔符 os。 linesep 即可。
  如果换行符为''或'\ n',则不进行翻译   如果换行符是任何其他合法值,则写入的任何“\ n”字符都将转换为给定的字符串。

让我们看看:

>>> sys.stdout.newline = "\n"
>>>

好的,那么

import sys
sys.stdout.newline = '\n'
print("one\ntwo")

不起作用:

one\r\n
two\r\n

因为该属性不存在:

>>> sys.stdout.newline
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: '_io.TextIOWrapper' object has no attribute 'newline'

我之前应该检查过..

>>> vars(sys.stdout)
{'mode': 'w'}

实际上,我们没有重新定义的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']

不是。

但我们至少可以将缓冲区的默认接口替换为指定所需的换行符:

import sys, io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, newline='\n' )
print("one\ntwo")

最终导致

one\n
two\n

要恢复,只需将sys.stdout重新分配给您制作的副本即可。或者,显然不推荐使用内部保留sys.__stdout__来做到这一点。

警告:请参阅下面的eryksun's comment,这需要一些小心。请改用他的解决方案(链接如下):

似乎也可以重新打开该文件,请参阅Wrap an open stream with io.TextIOWrapper获取灵感,并获得此答案https://stackoverflow.com/a/34997357/1619432

如果你想仔细看看,请查看Python(CPython)来源: https://github.com/python/cpython/blob/master/Modules/_io/textio.c

还有os.linesep,让我们看看它是否真的是“\ r \ n”for Windows:

>>> import os
>>> os.linesep
'\r\n'
>>> ",".join([f'0x{ord(c):X}' for c in os.linesep])
'0xD,0xA'

这可以重新定义吗?

#!python3
#coding=utf-8
import sys, os
saved = os.linesep
os.linesep = '\n'
print(os.linesep)
print("one\ntwo")
os.linesep = saved

它可以在交互模式下,但显然不是这样:

\r\n
\r\n
one\r\n
two\r\n