【问题标题】:Split unicode string into 300 byte chunks without destroying characters在不破坏字符的情况下将 unicode 字符串拆分为 300 字节的块
【发布时间】:2011-08-27 23:01:09
【问题描述】:

我想在不破坏任何字符的情况下将u"an arbitrary unicode string" 拆分为 300 个字节的块。字符串将被写入使用unicode_string.encode("utf8") 的需要utf8 的套接字。我不想破坏任何角色。我该怎么做?

【问题讨论】:

    标签: python string utf-8


    【解决方案1】:

    如果您可以确保您的字符的 utf-8 表示只有 2 个字节长,那么您应该可以安全地将 unicode 字符串拆分为 150 个字符的块(对于大多数欧洲编码来说,这应该是正确的)。但是 utf-8 是可变宽度编码。因此可能会将 unicode 字符串拆分为单个字符,将每个 char 转换为 utf-8 并填充缓冲区,直到达到最大块大小...如果必须高吞吐量,这可能效率低下并且是个问题...

    【讨论】:

    • 欧洲编码会更多。中国人和日本人肯定都会有代表。不需要高吞吐量。我希望有一个更漂亮的解决方案。
    • 嗯,utf-8 转换的字符最多 4 个字节长。所以 75 个字符乘以 4 的垃圾是 300 个字节 - 为了非常安全。
    【解决方案2】:

    UTF-8 有一个特殊属性,即所有连续字符都是0x800xBF(从第 10 位开始)。所以只要确保你没有在一个之前分裂。

    类似的东西:

    def split_utf8(s, n):
        if len(s) <= n:
            return s, None
        while ord(s[n]) >= 0x80 and ord(s[n]) < 0xc0:
            n -= 1
        return s[0:n], s[n:]
    

    应该可以解决问题。

    注意:它是在编码值上完成的,即python 2中的str和python 3中的bytes。python 3 bytes.__getitem__还包括对ord的调用,所以只需放弃它在那里。

    【讨论】:

    • 不适用于 Python 3,其中 len(s) 为您提供字符串中的 Unicode 字符数,而 n 是所需的 UTF-8 字节数。
    • @RogerDahl,它在 python 2 和 python 3 中的工作方式完全相同——在这两种情况下,它都应在 .encode() 之后调用,len(bytes) 返回字节数(在答案中添加了注释以明确这一点)。
    • 谢谢,Jan,但不能将 bytes 传递到函数中,因为 s[n] 然后返回一个 int ord 无法处理。
    • @RogerDahl,好的,这有点违反直觉,但解决方案很简单。由于ord 将字符或字节转换为整数,而您已经有整数,只需删除ord
    【解决方案3】:

    UTF-8 就是为此而设计的。

    def split_utf8(s, n):
        """Split UTF-8 s into chunks of maximum length n."""
        while len(s) > n:
            k = n
            while (ord(s[k]) & 0xc0) == 0x80:
                k -= 1
            yield s[:k]
            s = s[k:]
        yield s
    

    未测试。但是你找到一个分割的地方,然后原路返回,直到你到达一个字符的开头。

    但是,如果用户可能想要查看单个块,您可能希望改为在字素簇边界上进行拆分。这要复杂得多,但并非难以处理。例如,在"é" 中,您可能不想拆分"e""´"。或者你可能不在乎,只要他们最终再次粘在一起。

    【讨论】:

    • 我对此进行了测试,它似乎有效。虽然最终用户会看到这些块,但我发现这个解决方案已经足够好了。在一些快速阅读字素集群边界似乎非常难以实施。我现在不需要它。 (ord(s[k]) &amp; 0xc0) == 0x80"\x80" &lt;= s[k] &lt;= "\xBF" 一样吗?我确实觉得这个做工精巧
    • 是的,它们是等价的。我有点习惯用 C 编写 UTF-8 处理代码,这解释了这种风格。使用任何你觉得最好的风格。
    【解决方案4】:

    使用设计为每个字符固定长度的 unicode 编码,例如utf-32

    >>> u_32 = u'Юникод'.encode('utf-32')
    >>> u_32
    '\xff\xfe\x00\x00.\x04\x00\x00=\x04\x00\x008\x04\x00\x00:\x04\x00\x00>\x04\x00\x
    004\x04\x00\x00'
    >>> len(u_32)
    28
    >>> len(u_32)%4
    0
    >>>
    

    编码后,您可以发送任意大小的块(大小必须是 4 字节的倍数)而不会破坏字符

    【讨论】:

    • 这是不可能的,因为服务器需要 UTF-8
    【解决方案5】:

    经过测试。

    def split_utf8(s , n):
        assert n >= 4
        start = 0
        lens = len(s)
        while start < lens:
            if lens - start <= n:
                yield s[start:]
                return # StopIteration
            end = start + n
            while '\x80' <= s[end] <= '\xBF':
                end -= 1
            assert end > start
            yield s[start:end]
            start = end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多