我已经尝试了许多解决方案,我已经阅读了许多网站,但我似乎无法解决这个问题。我有一个包含消息对象的文件。每条消息都有一个4字节值,即消息类型,一个4字节值,即长度,然后是消息数据,即Unicode中的ASCII。当我打印到屏幕上时,它看起来像ASCII。当我将输出定向到一个文件时,我得到了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))}")
用户可以使用此link获取要解码的文件。
发布于 2021-03-19 19:06:11
在写入控制台和写入文件时,sys.stdout
的行为似乎有所不同。手册(https://docs.python.org/3/library/sys.html#sys.stdout)说这是意料之中的,但只给出了Windows的细节。
在任何情况下,您都要将unicode写入标准输出(通过print()
),这就是在文件中获取unicode的原因。可以通过不对getFields
中的消息进行解码来避免这种情况(因此可以用fields = msg.split(b';')
替换fields = getFields(msg)
,并使用sys.stdout.buffer.write(field+b'\n')
写入标准输出。
显然,混合使用print()
和sys.stdout.buffer.write()
存在一些问题,因此Python 3: write binary to stdout respecting buffering可能值得一读。
tl;dr -尝试在完全不解码为unicode的情况下写入字节。
发布于 2021-03-19 19:22:59
简而言之,定义一个自定义函数,并在调用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
编码。
请注意,print的文档说明:
由于打印的参数被转换为文本字符串,因此print()
不能与二进制模式文件对象一起使用。对于这些,请改用file.write(...)
。
现在,如果您要重定向stdout
,可以直接在sys.stdout中调用write()
。然而,正如文档所解释的那样:
要在标准流中写入或读取二进制数据,请使用底层的二进制
buffer
对象。例如,要将字节写入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()
注册的任何其他名称。
由于目标输出是纯文本,因此'backslashreplace'
可能是保持无损输出的最佳方法。但是,如果您不关心保留非ASCII码字符,'ignore'
也可以工作。
ascii_msg = f"Message: {codecs.decode(msg, 'utf-8')}".encode('ascii', errors='backslashreplace')
sys.stdout.buffer.write(ascii_msg)
是的,对于发送到print
的每个字符串,您都需要这样做。定义一个自定义的打印函数来保持代码的可读性可能是有意义的:
def ascii_print(txt):
sys.stdout.buffer.write(txt.encode('ascii', errors='backslashreplace'))
然后在你的代码中,你可以直接调用它,而不是print
ascii_print(f"Message: {codecs.decode(msg, 'utf-8')}")
https://stackoverflow.com/questions/66713175
复制