Python 将unicode字符串拆分为300字节的块,而不销毁字符

Python 将unicode字符串拆分为300字节的块,而不销毁字符,python,string,utf-8,Python,String,Utf 8,我想将u“任意unicode字符串”拆分为300字节的块,而不破坏任何字符。这些字符串将被写入一个套接字,该套接字使用unicode\u string.encode(“utf8”)预期使用utf8。我不想破坏任何角色。我该怎么做?如果您可以确保字符的utf-8表示长度仅为2字节,那么将unicode字符串拆分为150个字符的块应该是安全的(对于大多数欧洲编码,这应该是正确的)。但是utf-8是可变宽度编码。因此,可能会将unicode字符串拆分为单个字符,将每个字符转换为utf-8,并填充缓冲区

我想将
u“任意unicode字符串”
拆分为300字节的块,而不破坏任何字符。这些字符串将被写入一个套接字,该套接字使用
unicode\u string.encode(“utf8”)
预期使用utf8。我不想破坏任何角色。我该怎么做?

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

utf-8具有一个特殊属性,即所有连续字符都是
0x80
0xBF
(从第10位开始)。所以只要确保不要在第1位之前拆分

大致如下:

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:]
def拆分\u utf8(s,n):
如果len(s)=0x80且ord(s[n])<0xc0:
n-=1
返回s[0:n],s[n:]
我们应该做到这一点


注意:这是在编码值上完成的,即Python2中的
str
和Python3中的
bytes
。Python3中的
字节。uuu getitem\uuuu
还包括对
ord
的调用,因此只需将其放到那里。

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
未测试。但您找到一个拆分的位置,然后回溯到角色的开头


但是,如果用户可能希望看到单个块,您可能希望在grapheme群集边界上进行拆分。这要复杂得多,但并不难处理。例如,在
“é”
,您可能不希望拆分
“e”
“'
。或者你可能不在乎,只要它们最终再次粘在一起。

使用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字节的倍数),而无需销毁已测试的字符

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
def拆分\u utf8(s,n):
断言n>=4
开始=0
镜头=镜头
启动时<镜头:

如果lens-start,将有更多的欧洲编码。中文和日文都将明确表示。不需要高吞吐量。但我希望有一个更漂亮的解决方案。好吧,utf-8转换字符最多4字节长。因此75个字符乘以4的垃圾将产生300字节-为了非常安全。这不是p可能是因为服务器期望UTF-8I测试了它,它似乎可以工作。虽然最终用户会看到块,但我觉得这个解决方案足够好。在一些快速阅读后,grapheme集群边界似乎非常需要实现。我现在不需要它。是否
(ord(s[k])&0xc0)=0x80
“\x80”相同是的,它们是等效的。我有点习惯于用C编写UTF-8处理代码,这就解释了这种风格。使用任何你觉得最好的风格。不适用于Python 3,其中
len(s)
给出字符串中的Unicode字符数,而
n
是所需的UTF-8字节数。@RogerDahl,它在python 2和python 3中的工作原理完全相同-在这两种情况下,它都应在
之后调用。encode()
,而
len(bytes)
返回字节数(在答案中添加注释以澄清这一点)。谢谢,Jan,但无法将
字节
传递到函数中,因为
s[n]
然后返回一个
int
,而
ord
无法处理。@RogerDahl,好吧,这有点违反直觉,但解决方法很简单。由于
ord
将字符或字节转换为整数,而您已经有了整数,只需删除
ord