如何在Python中从其名称中确定Unicode字符,即使该字符是控制字符?

时间:2011-07-05 22:31:50

标签: python unicode

我想创建一个Unicode代码点数组,这些代码点构成JavaScript中的空白区域(减去我分别处理的Unicode白空间代码点)。这些字符包括水平制表符,垂直制表符,换页符,空格,不间断空格和BOM。我可以用魔术数字来做到这一点:

whitespace = [0x9, 0xb, 0xc, 0x20, 0xa0, 0xfeff]

这有点模糊;名字会更好。通过unicodedata.lookup传递的ord方法有助于某些方法:

>>> ord(unicodedata.lookup("NO-BREAK SPACE"))
160

但这不适用于0x9,0xb或0xc - 我认为因为它们是控制字符,而“名称”FORM FEED等只是别名。有没有办法将这些“名称”映射到标准Python中的字符或其代码点?或者我运气不好?

7 个答案:

答案 0 :(得分:13)

Kerrek SB的评论很好:只需将名字放在评论中即可。

BTW,Python还支持命名的unicode文字:

>>> u"\N{NO-BREAK SPACE}"
u'\xa0'

但是它使用相同的unicode名称数据库,并且控制字符不在其中。

答案 1 :(得分:2)

您可以通过解析Unicode public directory中的几行UCD文件来为控制字符滚动自己的“数据库”。特别是,请参阅UnicodeData-6.1.0d3文件(或查看早期版本的父目录)。

答案 2 :(得分:2)

我不认为它可以在标准Python中完成。 unicodedata模块使用UnicodeData.txt v5.2.0 Unicode数据库。请注意,控制字符都分配了名称<control>(第二个字段,以分号分隔)。

Python源代码分发中的脚本Tools/unicode/makeunicodedata.py用于生成Python运行时使用的表。 makeunicodename函数如下所示:

def makeunicodename(unicode, trace):

    FILE = "Modules/unicodename_db.h"

    print "--- Preparing", FILE, "..."

    # collect names
    names = [None] * len(unicode.chars)

    for char in unicode.chars:
        record = unicode.table[char]
        if record:
            name = record[1].strip()
            if name and name[0] != "<":
                names[char] = name + chr(0)
    ...

请注意,它会跳过名称以"<"开头的条目。因此,没有名称可以传递给unicodedata.lookup,而这些名称会返回给其中一个控制字符。

只需对水平制表符,换行符和回车符的代码点进行硬编码,然后留下描述性注释。正如Zen of Python所说,“实用性胜过纯洁”。

答案 3 :(得分:1)

几点:

(1)“BOM”不是一个字符。 BOM是一个出现在文件开头的字节序列,用于指示以UTF-nn编码的文件的字节顺序。 BOM是你'\ uFEFF'.encode('UTF-nn')。使用适当的编解码器读取文件将会破坏BOM;你不认为它是一个Unicode字符。 BOM不是数据。如果您确实在数据中看到了'\ uFEFF',请将其视为(已弃用)ZERO-WIDTH NO-BREAK SPACE。

(2)“减去Unicode-white-space代码点,我单独解决”?不是NO-BREAK SPACE是一个“Unicode-white-space”代码点?

(3)你的Python似乎被打破了;我这样做:

>>> ord(unicodedata.lookup("NO-BREAK SPACE"))
160

(4)你可以使用前三个的转义序列。

>>> map(hex, map(ord, "\t\v\f"))
['0x9', '0xb', '0xc']

(5)您可以使用" "作为第四个。

(6)即使你可以使用名字,你的代码的读者仍然会盲目相信,例如: “FORM FEED”是一个空格字符。

(7)\r\n发生了什么?

答案 4 :(得分:0)

假设您正在使用Unicode字符串,则在使用正则表达式时,列表中的前五项以及所有其他Unicode空格字符将与\s选项匹配。使用Python 3.1.2:

>>> import re
>>> s = '\u0009,\u000b,\u000c,\u0020,\u00a0,\ufeff'
>>> s
'\t,\x0b,\x0c, ,\xa0,\ufeff'
>>> re.findall(r'\s', s)
['\t', '\x0b', '\x0c', ' ', '\xa0']

至于字节顺序标记,给定的标记可以称为codecs.BOM_BEcodecs.BOM_UTF16_BE(尽管在Python 3+中,它作为bytes对象返回,而不是str)。

答案 5 :(得分:0)

新行的official Unicode recommendation可能与Python codecs模块处理换行符的方式不一致。由于u'\n'通常被称为“新行”,因此人们可能会根据此建议将Python字符串u'\n'表示为字符U+2028 LINE SEPARATOR并按此编码,而不是无语义控制字符U+000A。但我只能想象如果codecs模块实际实现了该策略会产生的混淆,并且除此之外还有有效的反驳。同样适用于水平/垂直制表符和换页符,它们可能不是真正的字符,但无论如何都要控制。 (我当然会认为退格是一个控件,而不是一个角色。)

您的问题似乎假设将U+000A视为控制字符(而不是行分隔符)是错误的;但这完全不确定。对于各地的文本处理应用程序而言,假设传统的打印机压板滚动控制信号确实是真正的“行分隔符”可能更为错误。

答案 6 :(得分:-1)

您可以扩展查找功能以处理未包含的字符。

def unicode_lookup(x):
    try:
        ch = unicodedata.lookup(x)
    except KeyError:
        control_chars = {'LINE FEED':unichr(0x0a),'CARRIAGE RETURN':unichr(0x0d)}
        if x in control_chars:
            ch = control_chars[x]
        else:
            raise
    return ch

>>> unicode_lookup('SPACE')
u' '
>>> unicode_lookup('LINE FEED')
u'\n'
>>> unicode_lookup('FORM FEED')

Traceback (most recent call last):
  File "<pyshell#17>", line 1, in <module>
    unicode_lookup('FORM FEED')
  File "<pyshell#13>", line 3, in unicode_lookup
    ch = unicodedata.lookup(x)
KeyError: "undefined character name 'FORM FEED'"