【问题标题】:Reading UTF-8 strings from a binary file从二进制文件中读取 UTF-8 字符串
【发布时间】:2013-03-04 10:43:29
【问题描述】:

我有一些文件包含一堆不同类型的二进制数据,我正在编写一个模块来处理这些文件。

其中,它包含以下格式的 UTF-8 编码字符串:2 字节大端 stringLength(我使用 struct.unpack() 解析),然后是字符串。由于它是 UTF-8,字符串的字节长度可能大于 stringLength 并且如果字符串包含多字节字符(更不用说弄乱所有文件中的其他数据)。

如何从文件中读取 n 个 UTF-8 字符(不同于 n 个字节),同时了解 UTF-8 的多字节属性?我已经用谷歌搜索了半个小时,我发现的所有结果要么不相关,要么做出了我无法做出的假设。

【问题讨论】:

  • 您确定 stringLength 是字符而不是字节吗?
  • 哇,那真是一种可怕的格式。您是否已将数据读入字符串或某种列表?可以很容易地检查 UTF-8 字节以确定一个字符后跟多少字节,但是您需要逐个解码字符处理这些字符。
  • @GrahamBorland 100%?不,我还没有找到实际使用多字节字符的文件,但这是我对规范的解释。
  • @MartijnPieters 好的,我如何在 Python 中做到这一点?有什么方便的模块可以使用吗?

标签: python utf-8


【解决方案1】:

给定一个文件对象和多个字符,您可以使用:

# build a table mapping lead byte to expected follow-byte count
# bytes 00-BF have 0 follow bytes, F5-FF is not legal UTF8
# C0-DF: 1, E0-EF: 2 and F0-F4: 3 follow bytes.
# leave F5-FF set to 0 to minimize reading broken data.
_lead_byte_to_count = []
for i in range(256):
    _lead_byte_to_count.append(
        1 + (i >= 0xe0) + (i >= 0xf0) if 0xbf < i < 0xf5 else 0)

def readUTF8(f, count):
    """Read `count` UTF-8 bytes from file `f`, return as unicode"""
    # Assumes UTF-8 data is valid; leaves it up to the `.decode()` call to validate
    res = []
    while count:
        count -= 1
        lead = f.read(1)
        res.append(lead)
        readcount = _lead_byte_to_count[ord(lead)]
        if readcount:
            res.append(f.read(readcount))
    return (''.join(res)).decode('utf8')

测试结果:

>>> test = StringIO(u'This is a test containing Unicode data: \ua000'.encode('utf8'))
>>> readUTF8(test, 41)
u'This is a test containing Unicode data: \ua000'

在 Python 3 中,将文件对象包装在 io.TextIOWrapper() object 中并将解码留给原生且高效的 Python UTF-8 实现当然要容易得多

【讨论】:

  • 正是我正在寻找的。接受并赞成。当我将该部分归属于您时,我链接到您的 StackOverflow 个人资料就足够了吗?
  • @Surma:当然;这个网站的所有内容都被许可为 CC-wiki(见右下角),但 readcount 'function' 改编自 simple C macro,所以我也在重用东西。 :-) 总而言之,一旦您了解了底层字节格式,这就是简单的事情。
  • @Surma:还有更多的pythonic方法来确定readcount的值;它们甚至可能比我在这里使用的更快。这个每个字节使用 2 到 4 个(简单)测试。
  • @Surma:更新:改为使用表格,因此您的内部循环只需对每个前导字节进行一次查找。
  • @Sovetnikov:只需使用bytes(res).decode()。或者,不要自己处理原始 UTF-8 字节,而是使用标准库 io.TextIOWrapper() 对象。
【解决方案2】:

UTF-8 中的一个字符可以是 1byte,2bytes,3byte3。

如果您必须逐字节读取文件,则必须遵循 UTF-8 编码规则。 http://en.wikipedia.org/wiki/UTF-8

大多数情况下,您可以将编码设置为 utf-8,然后读取输入流。

你不需要关心你读了多少字节。

【讨论】:

  • 我用谷歌搜索设置输入流编码并获得了codecs 模块上的文档。如果我理解正确,我可以这样做:strLen = struct.unpack('&gt;h', f.read(2)) utfStream = codecs.open(f, 'r', 'utf-8') string = utfStream.read(strLen) 不过有一个问题:这是否会推进我的文件描述符中的指针,以便f 上的后续read() 将返回字节after 我刚刚读到的字符串?编辑:我的代码示例中的换行符去哪儿了?
猜你喜欢
  • 2021-09-19
  • 2016-07-01
  • 2014-06-18
  • 2012-09-05
  • 2018-08-01
  • 2014-10-26
  • 1970-01-01
  • 2020-10-30
相关资源
最近更新 更多