将 stdout 重定向到具有 unicode 编码的文件,同时将 windows eol 保留在 python 2
Redirect stdout to a file with unicode encoding while keeping windows eol in python 2
我在这里碰壁了。我需要将所有输出重定向到一个文件,但我需要将此文件编码为 utf-8。问题是当使用 codecs.open
:
# errLog = io.open(os.path.join(os.getcwdu(),u'BashBugDump.log'), 'w',
# encoding='utf-8')
errLog = codecs.open(os.path.join(os.getcwdu(), u'BashBugDump.log'),
'w', encoding='utf-8')
sys.stdout = errLog
sys.stderr = errLog
codecs 以二进制模式打开文件,导致 \n
行终止符。我尝试使用 io.open
但这并不适用于整个代码库中使用的打印语句(参见 Python 2.7: print doesn't speak unicode to the io module? or python: TypeError: can't write str to text stream)
我不是唯一遇到此问题的人,例如,请参阅 here but the solution they adopted 特定于我们不使用的日志记录模块。
另请参阅这不会修复 python 中的错误:https://bugs.python.org/issue2131
那么在 python2 中执行此操作的正确方法是什么?
看来 io
的 Python 2 版本不能很好地与 print
语句配合使用,但是如果您使用 print
函数它会起作用.
演示:
from __future__ import print_function
import sys
import io
errLog = io.open('test.log', mode='wt', buffering=1, encoding='utf-8', newline='\r\n')
sys.stdout = errLog
print(u'This is a ™ test')
print(u'Another © line')
'test.log'
的内容
This is a ™ test
Another © line
'test.log'
的十六进制转储
00000000 54 68 69 73 20 69 73 20 61 20 e2 84 a2 20 74 65 |This is a ... te|
00000010 73 74 0d 0a 41 6e 6f 74 68 65 72 20 c2 a9 20 6c |st..Another .. l|
00000020 69 6e 65 0d 0a |ine..|
00000025
我 运行 此代码 Python 2.6 Linux, YMMV.
如果不想使用print
功能,可以实现自己的类文件编码class.
import sys
class Encoder(object):
def __init__(self, fname):
self.file = open(fname, 'wb')
def write(self, s):
self.file.write(s.replace('\n', '\r\n').encode('utf-8'))
errlog = Encoder('test.log')
sys.stdout = errlog
sys.stderr = errlog
print 'hello\nthere'
print >>sys.stderr, u'This is a ™ test'
print u'Another © line'
print >>sys.stderr, 1, 2, 3, 4
print 5, 6, 7, 8
'test.log'
的内容
hello
there
This is a ™ test
Another © line
1 2 3 4
5 6 7 8
'test.log'
的十六进制转储
00000000 68 65 6c 6c 6f 0d 0a 74 68 65 72 65 0d 0a 54 68 |hello..there..Th|
00000010 69 73 20 69 73 20 61 20 e2 84 a2 20 74 65 73 74 |is is a ... test|
00000020 0d 0a 41 6e 6f 74 68 65 72 20 c2 a9 20 6c 69 6e |..Another .. lin|
00000030 65 0d 0a 31 20 32 20 33 20 34 0d 0a 35 20 36 20 |e..1 2 3 4..5 6 |
00000040 37 20 38 0d 0a |7 8..|
00000045
请记住,这只是一个快速演示。您可能需要一种更复杂的方法来处理换行符,例如,如果 \n
前面已经有 \r
,您可能不想替换它。 OTOH,使用正常的 Python 文本处理应该不是问题...
这是结合了前面两种策略的另一个版本。不知道比第二版快不快
import sys
import io
class Encoder(object):
def __init__(self, fname):
self.file = io.open(fname, mode='wt', encoding='utf-8', newline='\r\n')
def write(self, s):
self.file.write(unicode(s))
errlog = Encoder('test.log')
sys.stdout = errlog
sys.stderr = errlog
print 'hello\nthere'
print >>sys.stderr, u'This is a ™ test'
print u'Another © line'
print >>sys.stderr, 1, 2, 3, 4
print 5, 6, 7, 8
这产生与以前版本相同的输出。
选项 1
重定向是一个 shell 操作。您根本不必更改 Python 代码,但您必须告诉 Python 如果重定向要使用什么编码。这是通过环境变量完成的。以下代码将 stdout 和 stderr 重定向到一个 UTF-8 编码的文件:
test.bat
set PYTHONIOENCODING=utf8
python test.py >out.txt 2>&1
test.py
#coding:utf8
import sys
print u"我不喜欢你女朋友!"
print >>sys.stderr, u"你需要一个新的。"
out.txt(以 UTF-8 编码)
我不喜欢你女朋友!
你需要一个新的。
out.txt
的十六进制转储
0000: E6 88 91 E4 B8 8D E5 96 9C E6 AC A2 E4 BD A0 E5
0010: A5 B3 E6 9C 8B E5 8F 8B EF BC 81 0D 0A E4 BD A0
0020: E9 9C 80 E8 A6 81 E4 B8 80 E4 B8 AA E6 96 B0 E7
0030: 9A 84 E3 80 82 0D 0A
注意:您确实需要打印 Unicode 字符串才能工作。打印字节串,你得到你打印的字节。
选项 2
codecs.open
可能会强制二进制模式,但 codecs.getwriter
不会。给它一个以文本模式打开的文件:
#coding:utf8
import sys
import codecs
sys.stdout = sys.stderr = codecs.getwriter('utf8')(open('out.txt','w'))
print u"我不喜欢你女朋友!"
print >>sys.stderr, u"你需要一个新的。"
(与上面相同的输出和 hexdump)
我在这里碰壁了。我需要将所有输出重定向到一个文件,但我需要将此文件编码为 utf-8。问题是当使用 codecs.open
:
# errLog = io.open(os.path.join(os.getcwdu(),u'BashBugDump.log'), 'w',
# encoding='utf-8')
errLog = codecs.open(os.path.join(os.getcwdu(), u'BashBugDump.log'),
'w', encoding='utf-8')
sys.stdout = errLog
sys.stderr = errLog
codecs 以二进制模式打开文件,导致 \n
行终止符。我尝试使用 io.open
但这并不适用于整个代码库中使用的打印语句(参见 Python 2.7: print doesn't speak unicode to the io module? or python: TypeError: can't write str to text stream)
我不是唯一遇到此问题的人,例如,请参阅 here but the solution they adopted 特定于我们不使用的日志记录模块。
另请参阅这不会修复 python 中的错误:https://bugs.python.org/issue2131
那么在 python2 中执行此操作的正确方法是什么?
看来 io
的 Python 2 版本不能很好地与 print
语句配合使用,但是如果您使用 print
函数它会起作用.
演示:
from __future__ import print_function
import sys
import io
errLog = io.open('test.log', mode='wt', buffering=1, encoding='utf-8', newline='\r\n')
sys.stdout = errLog
print(u'This is a ™ test')
print(u'Another © line')
'test.log'
的内容This is a ™ test
Another © line
'test.log'
的十六进制转储00000000 54 68 69 73 20 69 73 20 61 20 e2 84 a2 20 74 65 |This is a ... te|
00000010 73 74 0d 0a 41 6e 6f 74 68 65 72 20 c2 a9 20 6c |st..Another .. l|
00000020 69 6e 65 0d 0a |ine..|
00000025
我 运行 此代码 Python 2.6 Linux, YMMV.
如果不想使用print
功能,可以实现自己的类文件编码class.
import sys
class Encoder(object):
def __init__(self, fname):
self.file = open(fname, 'wb')
def write(self, s):
self.file.write(s.replace('\n', '\r\n').encode('utf-8'))
errlog = Encoder('test.log')
sys.stdout = errlog
sys.stderr = errlog
print 'hello\nthere'
print >>sys.stderr, u'This is a ™ test'
print u'Another © line'
print >>sys.stderr, 1, 2, 3, 4
print 5, 6, 7, 8
'test.log'
的内容hello
there
This is a ™ test
Another © line
1 2 3 4
5 6 7 8
'test.log'
的十六进制转储00000000 68 65 6c 6c 6f 0d 0a 74 68 65 72 65 0d 0a 54 68 |hello..there..Th|
00000010 69 73 20 69 73 20 61 20 e2 84 a2 20 74 65 73 74 |is is a ... test|
00000020 0d 0a 41 6e 6f 74 68 65 72 20 c2 a9 20 6c 69 6e |..Another .. lin|
00000030 65 0d 0a 31 20 32 20 33 20 34 0d 0a 35 20 36 20 |e..1 2 3 4..5 6 |
00000040 37 20 38 0d 0a |7 8..|
00000045
请记住,这只是一个快速演示。您可能需要一种更复杂的方法来处理换行符,例如,如果 \n
前面已经有 \r
,您可能不想替换它。 OTOH,使用正常的 Python 文本处理应该不是问题...
这是结合了前面两种策略的另一个版本。不知道比第二版快不快
import sys
import io
class Encoder(object):
def __init__(self, fname):
self.file = io.open(fname, mode='wt', encoding='utf-8', newline='\r\n')
def write(self, s):
self.file.write(unicode(s))
errlog = Encoder('test.log')
sys.stdout = errlog
sys.stderr = errlog
print 'hello\nthere'
print >>sys.stderr, u'This is a ™ test'
print u'Another © line'
print >>sys.stderr, 1, 2, 3, 4
print 5, 6, 7, 8
这产生与以前版本相同的输出。
选项 1
重定向是一个 shell 操作。您根本不必更改 Python 代码,但您必须告诉 Python 如果重定向要使用什么编码。这是通过环境变量完成的。以下代码将 stdout 和 stderr 重定向到一个 UTF-8 编码的文件:
test.bat
set PYTHONIOENCODING=utf8
python test.py >out.txt 2>&1
test.py
#coding:utf8
import sys
print u"我不喜欢你女朋友!"
print >>sys.stderr, u"你需要一个新的。"
out.txt(以 UTF-8 编码)
我不喜欢你女朋友!
你需要一个新的。
out.txt
的十六进制转储0000: E6 88 91 E4 B8 8D E5 96 9C E6 AC A2 E4 BD A0 E5
0010: A5 B3 E6 9C 8B E5 8F 8B EF BC 81 0D 0A E4 BD A0
0020: E9 9C 80 E8 A6 81 E4 B8 80 E4 B8 AA E6 96 B0 E7
0030: 9A 84 E3 80 82 0D 0A
注意:您确实需要打印 Unicode 字符串才能工作。打印字节串,你得到你打印的字节。
选项 2
codecs.open
可能会强制二进制模式,但 codecs.getwriter
不会。给它一个以文本模式打开的文件:
#coding:utf8
import sys
import codecs
sys.stdout = sys.stderr = codecs.getwriter('utf8')(open('out.txt','w'))
print u"我不喜欢你女朋友!"
print >>sys.stderr, u"你需要一个新的。"
(与上面相同的输出和 hexdump)