Запретить автоматическое преобразование новой строки в Python print() в CRLF в Windows
Я хотел бы передать текст с Unix-подобным EOL (LF) из Python через Windows CMD (консоль). Однако Python, похоже, автоматически преобразует отдельные символы новой строки в символы конца строки (EOL) в стиле Windows (т.е. \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
результаты в
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 ответ
Смотрите этот ответ: /questions/41410769/pechat-lf-s-python-3-na-standartnyij-vyivod-windows/41410774#41410774
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(). Вернуться к файлам объектов / потоков / текстовых файлов (краткая информация об объектах ввода-вывода в модели данных Python):
https://docs.python.org/3/glossary.html
Файловый объект, способный читать и записывать объекты str. Часто текстовый файл фактически получает доступ к байтово-ориентированному потоку данных и автоматически обрабатывает кодировку текста. Примерами текстовых файлов являются файлы, открытые в текстовом режиме ('r' или 'w'), sys.stdin, sys.stdout и экземпляры io.StringIO.
Так (как) можно переконфигурировать или открыть файл sys.stdout для управления поведением новой строки? И что именно?
>>> import sys
>>> type(sys.stdout)
<class '_io.TextIOWrapper'>
newline контролирует, как обрабатываются окончания строки. Это может быть None, '', '\n', '\r' и '\r\n'.
Это работает следующим образом:
При чтении ввода из потока, если символ новой строки равен None, включается режим универсальной новой строки. Строки на входе могут оканчиваться на "\ n", "\ r" или "\r\n", и они переводятся в "\ n" перед возвратом вызывающей стороне.
Если это '', включается универсальный режим перевода строки, но окончания строки возвращаются вызывающей стороне без перевода.
Если он имеет какие-либо другие допустимые значения, входные строки заканчиваются только данной строкой, а окончание строки возвращается вызывающей стороне без перевода.При записи вывода в поток, если символ новой строки равен None, любые записанные символы '\ 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
Комментарий ниже, это требует некоторой осторожности. Вместо этого используйте его решение (ссылка ниже):
Похоже, что также возможно повторно открыть файл, см. Обертывание открытого потока с io.TextIOWrapper для вдохновения, и этот ответ /questions/41410769/pechat-lf-s-python-3-na-standartnyij-vyivod-windows/41410774#41410774 для реализации.
Если вы хотите поближе познакомиться, ознакомьтесь с исходными текстами Python (CPython): https://github.com/python/cpython/blob/master/Modules/_io/textio.c
Также есть os.linesep, давайте посмотрим, действительно ли это "\r\n" для 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