在Python中生成随机的UTF-8字符串

29
我想测试我的代码对Unicode的处理能力。在random.choice()中,有没有任何东西可以选择整个Unicode范围,最好不要使用外部模块?谷歌和StackOverflow似乎都没有答案。
编辑:看起来这比预期的要复杂,所以我会重新表述问题——以下代码是否足以生成所有有效的非控制字符Unicode
unicode_glyphs = ''.join(
    unichr(char)
    for char in xrange(1114112) # 0x10ffff + 1
    if unicodedata.category(unichr(char))[0] in ('LMNPSZ')
    )

如果您能更详细地说明“测试我的代码的Unicode处理”,并解释生成随机UTF-8字符串在测试中扮演的角色,以及您认为“整个Unicode范围”是什么(16位?21位?非代理代码点?有效字符(例如不是U + FFFF)?)。您是否信任Python的UTF-8编解码器,或者您也需要进行测试?Python 2.X还是3.X还是两者兼顾? - John Machin
1
目标是在Python 2.6的Web界面中接受任何可打印的、有效的Unicode代码点(字符)作为输入。 - l0b0
2021年更新的通用Unicode代码点类别值表链接:https://www.unicode.org/reports/tr44/#GC_Values_Table - Matthew Willcockson
8个回答

25

由于问题标题可能是主要的搜索依据,因此以下是一种生成包含各种Unicode字符的随机字符串的方法。只需扩展示例中想要的代码点范围即可包含更多(或更少)可能的字符。

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))

谢谢你,Jacob。在Python 2.7中运行这段代码会有任何问题吗? - morfys
3
它本来没有,但我刚刚编辑过了,现在有了。谢谢你的询问。 - Jacob Wan

10

这将有助于确保程序在给出不正确的文本时不会崩溃,但它不能作为符合性测试的帮助。 - Esteban Küber
不用担心生成随机Unicode。借用别人的轮子比重新发明轮子更好。 - Matt Ball
4
回答不错,但实际上并没有按照所问的问题回答。 - Kylotan
在 Mac Chrome 54.0.2840.59 上下载该文件被阻止了,但您仍然可以通过单击它来查看它。 - Cat Zimmermann

9

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

#!/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标准的广泛性,我无法对其进行全面测试。另外请注意,这些字符并不是平均分布的(但序列中的每个字节都是如此)。

7

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

print(''.join(tuple(chr(i) for i in range(32, 0x110000) if chr(i).isprintable())))

所有可打印字符都包含在上面,甚至包括当前字体不打印的字符。and not chr(i).isspace()子句可以添加以过滤空格字符。

这实际上不会给你一个随机字符串,当然你可以使用 random.sample 而不是 print - l0b0
所以请使用 random.choices - gimboland

3
这取决于你想要多全面地进行测试以及你想要多准确地生成内容。Unicode是一个21位编码集(U+0000 .. U+10FFFF),但是该范围中有一些相当大的块被保留用于自定义字符。你是否需要担心在字符串开头生成组合字符(因为它们应该只出现在另一个字符之后)?
我采用的基本方法是随机生成Unicode代码点(例如U+2397或U+31232),在上下文中验证它(它是合法字符吗?它可以出现在这个字符串中吗?),并使用UTF-8编码有效的代码点。
如果你只想检查你的代码是否正确处理了格式错误的UTF-8,你可以使用更简单的生成方案。
请注意,你需要知道输入时期望得到的结果-否则你不是在测试,而是在试验。

0

回答修订后的问题:

是的,严格来说,“控制字符”的定义不包括 CR、LF 和 TAB;您是否需要这个?

请考虑回复我早前邀请,告诉我们您真正想要做什么。


0

由于Unicode只是一系列代码范围,那么使用unichr()获取0到0xFFFF之间的随机数对应的Unicode字符串如何呢?
(当然这只能得到一个代码点,如果需要则可以进行迭代)


3
很遗憾,情况并不是那么简单。Unicode包含的字符远远超过了0x100000个,并且范围并不相连。例如,代理值不能作为单个代码点出现。所以,什么构成有效的UTF-8字符串的问题非常复杂。细节在Unicode标准的第3章定义D92中有描述。此外,还有一张表(3-7)列出了所有有效的UTF-8字节序列可能性。 - Philipp
Unicode 的范围从 U+0000 到 U+10FFFF;还有许多无效的代码点,包括(碰巧)U+FFFF。Unicode 标准对它说:“<非字符> - 值 FFFF 保证根本不是 Unicode 字符”。 - Jonathan Leffler

0

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


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