关于unicode:在Python中生成随机UTF-8字符串

Generate random UTF-8 string in Python

我想测试代码的Unicode处理。我可以在random.choice()中放入任何东西来从整个Unicode范围中选择,最好不是外部模块?谷歌和StackOverflow似乎都没有答案。

编辑:这看起来比预期的要复杂,所以我将重新表述这个问题-下面的代码是否足以生成Unicode中所有有效的非控制字符?

1
2
3
4
5
unicode_glyphs = ''.join(
    unichr(char)
    for char in xrange(1114112) # 0x10ffff + 1
    if unicodedata.category(unichr(char))[0] in ('LMNPSZ')
    )


人们可能在这里找到他们的方法主要基于问题标题,所以这里有一种方法来生成包含各种Unicode字符的随机字符串。要包含更多(或更少)可能的字符,只需使用所需的代码点范围扩展示例的该部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import random

def get_random_unicode(length):

    try:
        get_char = unichr
    except NameError:
        get_char = chr

    # Update this to include code point ranges to be sampled
    include_ranges = [
        ( 0x0021, 0x0021 ),
        ( 0x0023, 0x0026 ),
        ( 0x0028, 0x007E ),
        ( 0x00A1, 0x00AC ),
        ( 0x00AE, 0x00FF ),
        ( 0x0100, 0x017F ),
        ( 0x0180, 0x024F ),
        ( 0x2C60, 0x2C7F ),
        ( 0x16A0, 0x16F0 ),
        ( 0x0370, 0x0377 ),
        ( 0x037A, 0x037E ),
        ( 0x0384, 0x038A ),
        ( 0x038C, 0x038C ),
    ]

    alphabet = [
        get_char(code_point) for current_range in include_ranges
            for code_point in range(current_range[0], current_range[1] + 1)
    ]
    return ''.join(random.choice(alphabet) for i in range(length))

if __name__ == '__main__':
    print('A random string: ' + get_random_unicode(10))


有一个来自MarkusKuhn的UTF-8压力测试可以使用。

还可以看到非常好的、不好的UTF-8示例测试数据。


下面是一个示例函数,它可能创建一个随机的格式良好的UTF-8序列,如Unicode 5.0.0的表3-7所定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/env python3.1

# From Table 3–7 of the Unicode Standard 5.0.0

import random

def byte_range(first, last):
    return list(range(first, last+1))

first_values = byte_range(0x00, 0x7F) + byte_range(0xC2, 0xF4)
trailing_values = byte_range(0x80, 0xBF)

def random_utf8_seq():
    first = random.choice(first_values)
    if first <= 0x7F:
        return bytes([first])
    elif first <= 0xDF:
        return bytes([first, random.choice(trailing_values)])
    elif first == 0xE0:
        return bytes([first, random.choice(byte_range(0xA0, 0xBF)), random.choice(trailing_values)])
    elif first == 0xED:
        return bytes([first, random.choice(byte_range(0x80, 0x9F)), random.choice(trailing_values)])
    elif first <= 0xEF:
        return bytes([first, random.choice(trailing_values), random.choice(trailing_values)])
    elif first == 0xF0:
        return bytes([first, random.choice(byte_range(0x90, 0xBF)), random.choice(trailing_values), random.choice(trailing_values)])
    elif first <= 0xF3:
        return bytes([first, random.choice(trailing_values), random.choice(trailing_values), random.choice(trailing_values)])
    elif first == 0xF4:
        return bytes([first, random.choice(byte_range(0x80, 0x8F)), random.choice(trailing_values), random.choice(trailing_values)])

print("".join(str(random_utf8_seq(),"utf8") for i in range(10)))

由于unicode标准的广泛性,我无法彻底测试它。还要注意,字符分布不均匀(但序列中的每个字节都是)。


以下代码打印任何可打印的UTF-8字符:

1
2
print(''.join(tuple(chr(l) for l in range(1, 0x10ffff)
                    if chr(l).isprintable())))

所有字符都存在,甚至那些不由使用的字体处理的字符也存在。为了过滤掉所有空格字符,可以添加and not chr(l).isspace()。(包括制表符)


这取决于你想做测试的彻底程度和你想做这一代测试的准确程度。完整地说,Unicode是一个21位代码集(U+0000..U+10FFFF)。但是,该范围中的一些相当大的块被预留给自定义字符。是否要担心在字符串开头生成组合字符(因为它们只应出现在另一个字符之后)?

我将采用的基本方法是随机生成一个Unicode代码点(比如U+2397或U+31232),在上下文中验证它(它是合法字符,它能出现在字符串中吗),并用UTF-8编码有效的代码点。

如果只想检查代码是否正确处理格式错误的UTF-8,可以使用更简单的生成方案。

请注意,您需要知道在给定输入的情况下期望什么——否则,您没有进行测试;您正在进行测试。


回答修改后的问题:

是的,对于"控制字符"的严格定义——请注意,您不会包括CR、LF和TAB;这是您想要的吗?

请考虑回复我先前的邀请,告诉我们你真正想做什么。


您可以下载一个用希腊语或德语编写的使用Unicode的网站,并将其提供给您的代码。


既然unicode只是一系列好代码,那么使用unichr()获取与0到0xffff之间的随机数对应的unicode字符串怎么样?(当然,这只会给出一个代码点,所以需要迭代)