我该如何使用Python将这个XML字符串转换成二进制格式?

3

首先,我正在解析一个文本文件,该文件以UTF-8编码保存在notepad中。这样做足以确保它是UTF-8吗?我尝试了chardet模块,但它并没有真正帮助我。如果有人能找出更多信息,以下是一些文本文件的几行:

CUSTOMERLOC|1|N/A|N/A|LEGACY COPPER|N/A|Existing|N/A|NRZ|NRZ|N/A|N/A
FTSMAR08|01/A|N/A|N/A|LEGACY COPPER|N/A|Existing|N/A|NRZ|NRZ|N/A|N/A
FTSMAR08|01/B|N/A|N/A|LEGACY COPPER|N/A|Existing|N/A|NRZ|NRZ|N/A|N/A

我使用lxml模块编写了XML,并使用tostring()方法将其分配给名为data的变量。

然后,我使用binascii模块的a2b_qp()函数将XML字符串转换为二进制,并将所有内容放入bytearray中。

data = bytearray(binascii.a2b_qp(ET.tostring(root, pretty_print=True)), "UTF-8")

在我看来,这个data变量应该包含我的XML二进制形式,它存储在一个bytearray里。

之后,我使用了一个更新游标,并将数据插入到表格的BLOB字段中。

row[2] = data
cursor.updateRow(row)

一切似乎都正常,但是当我使用以下代码读取BLOB字段时:

with arcpy.da.SearchCursor("Point", ['BlobField']) as cursor:
    for row in cursor:
        binaryRep = row[0]
        open("C:/Blob.xml, 'wb').write(binaryRep.tobytes())

当我打开 Blob.xml 文件时,我期望看到我最初创建的 XML 字符串以可读的形式呈现,但是我得到了这个混乱的结果,使用 UTF-8 编码设置 Notepad++:

enter image description here

而使用 ANSI 编码设置 Notepad++ 则出现以下混乱:

ANSI encoding

我认为有经验的人可能通过查看图片知道发生了什么。我已经阅读了很多并尝试解决问题,但是我一直被卡住了。

3个回答

4
我正在解析一个文本文件,我用记事本保存它的编码为UTF-8。这样就足以确保它是UTF-8编码了吗?我尝试使用chardet模块,但它并没有真正帮助我。
是的,告诉你的编辑器将其保存为特定的编码就足以确保它以该编码保存。如果可能的话,这也应该在文件中记录下来——在XML中,“”是一种常见的指定方法——但那只是元数据,并不能实际控制编码。chardet对于当你不知道编码时非常有用,但它所做的猜测工作应该作为最后的手段。UTF8通常是一个很好的默认假设,特别是对于XML。
这行代码的原因:
data = bytearray(binascii.a2b_qp(ET.tostring(root, pretty_print=True)), "UTF-8")

这段文字的问题在于它会执行一些不好的操作,最终导致 乱码。ET.tostring() 默认使用 ASCII 编码(因此会丢失任何不在 ASCII 范围内的数据,但这暂且不提)。所以现在你有了一个 ASCII 字符串。binascii.a2b_qp 使用可打印编码对其进行 解码。因此,它将把所有内容从可打印 ASCII 字符转换为不一定是可打印 ASCII 字符的内容(qp 将任何不在可打印 ASCII 范围内的字节编码为 3 个可打印 ASCII 字符)。这意味着,例如,如果您的文本中有任何内容说 =00,它将把它转换为空字节。问题在于你原来的内容 没有 经过 QP 编码,因此进行 QP 解码会得到无意义的内容。

然后您使用bytearray将其再次编码为UTF8。bytearray假定如果您给它一个编码,那么该字符串是一个Unicode字符串 - 您打破了这个假设并提供了原始二进制数据(这已经是无意义的)。将原始二进制数据编码为UTF8不是特别有意义的事情,此部分使我认为您正在使用Python 2。Python 3在尝试执行此操作时会正确抛出错误:

>>> bytearray(b'123', 'utf8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: encoding or errors without a string argument

Python 2在字节和解码字符方面的界限不够清晰,导致这种问题更容易出现。如果可以的话,升级到Python 3是一个非常好的选择。但对于a2b_qp(因为它是字节<->字节编码),之前得到的无意义结果也不会有所帮助。


解决方法是从一开始就使用UTF-8进行编码,不再考虑引用可打印格式。 (如果您真的想要将其编码为QP,请在其转换为UTF8之后运行它通过binascii.b2a)。
ElementTree允许您指定编码:
 ET.tostring(root, encoding='utf-8')

使用此方法将获得正确的UTF-8编码XML,可以在Notepad++中完美打开。


3

我认为你这里有些跑题:

binascii.a2b_qp(ET.tostring(root, pretty_print=True))

a2b_qp 假设输入是基于'quoted printable'(类似于base64)的,但实际上它是XML格式。结果二进制数据不正确。

相反,您应该使用 bytearray。将XML字符串及其编码("utf-8")传递给它,它会返回您的二进制数据。

编码是一组有趣的思维体操。简而言之:

  • 如果使用Python 3,则可能很好。 如果使用 2.x,则几乎肯定要使用unicode数据类型,而不是 str
  • Unicode 是比编码更高级的概念。每个可显示的字符都是一个(有时是多个)代码点,位于超过一百万个字符的巨大逻辑空间中。
  • 简单地将 Unicode 字符串写入磁盘需要为每个字符提供 3 个字节。这样的文件比必须的大得多,并且与大多数现有 ASCII 文件不兼容——在 1990 年代,大多数数据都是 ASCII 并且磁盘非常昂贵,因此使用了编码(映射)。UTF-8 是一个好选择,因为:
    • 向后兼容性:所有 7-位 ASCII 文件都是有效的 UTF-8 文件
    • 效率:8 位到 14 位字符(大多数人使用的其他字符)映射为 2 个字节的UTF-8。 其他字符按需要占用3或4个字节。
    • 兼容性:许多重要协议和标准使用 UTF-8。
  • 使用 binascii,您已进入另一种编码方式。这是一组例程,用于在需要发送二进制数据的媒介中(例如 JPG)仅允许使用 ASCII 或安全(例如 URL 和 SMTP/电子邮件)的情况下使用的。Base64 的工作方式如下:
    • 使用 A-Z、a-z、0-9 和其他一些字符,您有 64 个代码点或 6 位信息。
    • 其中 4 个字符是 6x4 =24 位,与3字节数据(3x8)相同。
    • 因此,Base64 将 3 字节的块映射为 4 个安全字符。
    • 换句话说,您可以将任何二进制数据转换为一块安全字符,但代价是增加30%的大小。

希望这有所帮助。


在这里使用bytearray不是正确的做法,有两个原因。写入二进制文件(假设Python 3或Python 2中使用io.open)需要一个bytes对象(Python 2中的str),你可以从bytearray中获取它,但也可以直接从(unicode)字符串中通过str.encode获取。更强的理由是ET.tostring已经给你了字节而不是unicode(尽管你可以显式地要求它为unicode)。如果没有编码,它默认为ASCII - 使用UTF8进行双重编码只会得到ASCII(如果它甚至尝试这样做 - 在Python 3中,bytearray(b'', encoding)是一个错误)。 - lvc

0

存储:

  • 准备好你的 XML 数据
  • 将其序列化为字符串
  • 将该字符串编码为 UTF-8 二进制字符串(即 xml_string.encode('utf-8')
  • 将生成的二进制字符串保存在数据库中

检索:

  • 从数据库中检索二进制字符串
  • 从 UTF-8 解码 - xml_string.decode('utf-8')
  • 再次反序列化为 XML
  • 对你的 XML 进行操作

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接