【问题标题】:Converting Unicode to ASCII in Python 3在 Python 3 中将 Unicode 转换为 ASCII
【发布时间】:2021-03-19 17:44:39
【问题描述】:

我尝试了许多解决方案,并且阅读了许多网站,但我似乎无法解决这个问题。我有一个包含消息对象的文件。每条消息都有一个 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来获取要解码的文件。

【问题讨论】:

  • 您提供的链接受密码保护。

标签: python python-3.x python-unicode


【解决方案1】:

似乎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: write binary to stdout respecting buffering 可能值得一读。

tl;dr - 尝试在不解码的情况下将字节写入 unicode。

【讨论】:

    【解决方案2】:

    简而言之,定义一个自定义函数并在您调用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')}")
    

    【讨论】:

    • Waylan,非常感谢,但我仍然在同一条船上。我尝试了您的 ascii_print 方法并尝试尽可能避免编码/解码。但与@cco 的回复一样,我能得到的最好结果是重定向到文件时,如果我对输出进行 cat 处理,一切看起来都很好。如果我用 vim 打开输出文件,我会在每个字符之间看到 ^@。如果我用 gvim 打开它,我会看到每个字符之间都有一个空格。所以我还没有把事情做好。
    • 您确定 vim 正在使用正确的编码打开文件吗?
    • 原来我最大的问题是使用 UTF-8 而不是 UTF-16。
    猜你喜欢
    • 1970-01-01
    • 2013-05-17
    • 2019-02-08
    • 2016-03-10
    • 1970-01-01
    • 2020-02-20
    • 2018-03-07
    • 1970-01-01
    • 2018-08-28
    相关资源
    最近更新 更多