Python Unicode - Windows 控制台可以打印哪些字符?

Python Unicode - What Characters Can Be Printed in Windows Console?

哪些 Unicode 字符可以在 Python 的 Windows 控制台中打印?

以下代码

for code in range(1114112):
    print(chr(code), end=",")

给出了令人印象深刻的结果,包括一个错误:

UnicodeEncodeError: 'utf-8' codec can't encode character '\ud800' in position 0: surrogates not allowed

然而,str 的文档声称值高达 0x110000 是允许的。

有没有办法让更多的字符显示出来?

为了回答你的问题,我们需要检查几层 Unicode。

有效的 Unicode 代码点是从 0 到 U+10FFFF。您可以使用 unicodedata.category(char) 查找具有 Unicode 代码点的类别。

从 U+D800 到 U+DFFF 的值是代理项,不应使用它们(在 UTF-16 中它们不能是 encoded/decoded)。 [它们用于增强 UCS-2(如此旧的 Unicode,直到 U+FFFF 都有代码点)到 UTF-16(到 U+10FFFF)。旧 programs/languages(如 Javascript)可能使用两个代理表示,而不是一个 UTF-16 代码点。

注意:Python允许使用它们是因为surrogateescape(主要用于阅读sys.argv),但您应该忽略它们,但仅在内部使用它们, 在正确转换它们之前。

因此,请勿尝试使用此类代码。

还有 非字符:U+FDD0–U+FDEF 和 FFFE 或 FFFF(即 U+FFFE、U+FFFF、U+1FFFE、U+ 1FFFF, … U+10FFFE, U+10FFFF) [from Wikipedia, Unicode], which should not be used, but ev. BOM (U+FEFF), but in this case only as first character. Reason: the first block: What's the purpose of the noncharacters U+FDD0 to U+FDEF?, others: 自动检测编码,所以我们不应该有 confusing 代码点:如果你检测到它们,您知道自己使用了错误的编码,并且更改了编码,直到获得有效的第一个代码点。

现在,使用 unicodedata.category(char),您还可以获得代码的类别(请参阅 Unicode character categories)。 U+1F、U+7F–U+9F之前的字符为控制字符,不打印。

您可能有格式化字符,可以修改附近的字符。

因此您可能希望排除 C*(注意:这将丢弃所有上述字符),也可能排除 Z*(空格)字符类别。

所以你有可打印的字符,由 unicodedata 标准模块知道。使用 unicodedata.unidata_version 检查数据库更新的 unicode 版本。你可能会。允许 Cn 类别(未分配):也许现在它们已分配。

但这还不够。您需要一种字体来显示这些字符。 Google 有 "No Tofu fonts" 这是(我认为)最完整的字体。

但这还不够。您只会得到字符的标准表示(可能不会,您应该在每个字符后添加一个 U+200C (ZWNJ),以强制字体不连接字符(例如在印度语中)。但是您会错过所有字符由代码点的组合表示:例如,许多重音字符、用圆圈或正方形括起来的字符、国家标志(您需要按正确顺序排列两个国家代码字符)等

注意:我很好奇如何从字体文件中获取所有字形,但这不是你的问题。

附录:

忘了说:组合字符不能单独显示,所以需要在e.g.使用 U+25CC,您可以使用 unicodedata.combining(chr).

检查它们

所以你可以使用这个代码

# if your console is not UTF-8 (or any unicode encoding) and python
# do no get it, you will get garbage
import unicodedata

combining = '\u25cc'
placeholder = '\ufffd'
zwnj = '\u200c'

line = ''
for code in range(0x10FFFF+1):
    c = chr(code)
    cat = unicodedata.category(c)
    if cat.startswith('C'):  # and cat != 'Cn':
        r = placeholder
    elif cat.startswith('Z'):
        r = ' '
    elif unicodedata.combining(c) > 0:
        r = combining + c + zwnj
    else:
        r = c + zwnj
    line += r
    if code % 256 == 255:
        print(line)
        line = ''