![](/img/trans.png)
[英]python, UnicodeEncodeError, converting unicode to ascii
[英]Converting Unicode to ASCII in Python 3
我尝试了许多解决方案,并且阅读了许多网站,但似乎无法解决这个问题。 我有一个包含消息对象的文件。 每条消息都有一个 4 字节的值是消息类型,一个 4 字节的值是长度,然后是 Unicode 中的 ASCII 的消息数据。 当我打印到屏幕上时,它看起来像 ASCII。 当我将 output 指向一个文件时,我得到 Unicode 所以我试图解码这一切的方式有些不对劲。 这是 python 脚本:
import sys
import codecs
import encodings.idna
import unicodedata
def getHeader(fileObj):
mstype_array = bytearray(4)
mslen_array = bytearray(4)
mstype = 0
mslen = 0
fileObj.seek(-1, 1)
mstype_array = fileObj.read(4)
mslen_array = fileObj.read(4)
mstype = int.from_bytes(mstype_array, byteorder=sys.byteorder)
mslen = int.from_bytes(mslen_array, byteorder=sys.byteorder)
return mstype,mslen
def getMessage(fileObj, count):
str = fileObj.read(count)#.decode("utf-8", "strict")
return str
def getFields(msg):
msg = codecs.decode(msg, 'utf-8')
fields = msg.split(';')
return fields
mstype = 0
mslen = 0
with open('../putty.log', 'rb') as f:
while True:
byte = f.read(1)
if not byte:
break
if byte == b'\x1D':
mstype, mslen = getHeader(f)
print (f"Msg Type: {mstype} Msg Len: {mslen}")
msg = getMessage(f, mslen)
print(f"Message: {codecs.decode(msg, 'utf-8')}")
#print(type(msg))
fields = getFields(msg)
print("Fields:")
for field in fields:
print(field)
else:
print (f"Char read: {byte} {hex(ord(byte))}")
使用可以使用此链接获取要解码的文件。
似乎sys.stdout
在写入控制台与写入文件时表现不同。 手册( https://docs.python.org/3/library/sys.html#sys.stdout )说这是预期的,但只提供了 Windows 的详细信息。
无论如何,您正在将 unicode 写入标准输出(通过print()
),这就是您在文件中获得 unicode 的原因。 您可以通过不解码getFields
中的消息来避免这种情况(因此您可以将fields = getFields(msg)
替换为fields = msg.split(b';')
并使用sys.stdout.buffer.write(field+b'\n')
。
混合print()
和sys.stdout.buffer.write()
显然存在一些问题,因此Python 3:将二进制写入 stdout 关于缓冲可能值得一读。
tl;dr - 尝试在不解码的情况下将字节写入 unicode。
简而言之,定义一个自定义 function 并在您调用print
的任何地方使用它。
import sys
def ascii_print(txt):
sys.stdout.buffer.write(txt.encode('ascii', errors='backslashreplace'))
ASCII 是 utf-8 的子集。 ACSII 字符与相同的 utf-8 编码字符无法区分。 在内部,所有 Python 字符串都是原始 Unicode。 但是,无法读取或写入原始 Unicode。 它们必须首先编码为某种编码。 默认情况下,在大多数系统上,默认编码是 utf-8,这是编码 Unicode 的最常见标准。
如果要使用不同的编码写出,则必须指定该编码。 我假设您出于某种原因需要ascii
编码。
请注意,打印状态的文档:
由于打印的 arguments 被转换为文本字符串,因此
print()
不能与二进制模式文件对象一起使用。 对于这些,请改用file.write(...)
。
现在,如果您正在重定向stdout
,您可以直接在sys.stdout中调用write()
。 但是,正如文档在那里解释的那样:
要从/向标准流写入或读取二进制数据,请使用底层二进制
buffer
object。 例如,要将字节写入stdout
,请使用sys.stdout.buffer.write(b'abc')
。
因此,而不是行print(f"Message: {codecs.decode(msg, 'utf-8')}")
,您可以这样做:
ascii_msg = f"Message: {codecs.decode(msg, 'utf-8')}".encode('ascii')
sys.stdout.buffer.write(ascii_msg)
请注意,我在字符串上专门调用了 str.encode并显式设置了ascii
编码。 另请注意,我编码了整个字符串(包括Message:
),而不仅仅是传入的变量(仍然需要解码)。 然后,您需要将该 ASCII 编码字节字符串直接写入sys.stdout.buffer
,如第二行所示。
这样做的一个问题是输入可能包含一些非 ASCII 字符。 照原样,会发生Unicodeerror
并且程序会崩溃。 为避免这种情况, str.encode
支持几种不同的错误处理选项:
其他可能的值是
'ignore'
、'replace'
、'xmlcharrefreplace'
、'backslashreplace'
和通过codecs.register_error()
注册的任何其他名称。
由于目标 output 是纯文本, 'backslashreplace'
可能是保持无损 output 的最佳方法。 但是,如果您不关心保留非 ASCII 字符, 'ignore'
也可以。
ascii_msg = f"Message: {codecs.decode(msg, 'utf-8')}".encode('ascii', errors='backslashreplace')
sys.stdout.buffer.write(ascii_msg)
是的,您需要为发送到print
的每个字符串执行此操作。 定义一个自定义打印 function 可能是有意义的,它使代码更具可读性:
def ascii_print(txt):
sys.stdout.buffer.write(txt.encode('ascii', errors='backslashreplace'))
然后在您的代码中,您可以只调用它而不是print
:
ascii_print(f"Message: {codecs.decode(msg, 'utf-8')}")
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.