Как определить символ Unicode по его имени в Python, даже если этот символ является управляющим символом? - PullRequest
10 голосов
/ 06 июля 2011

Я хотел бы создать массив кодовых точек Unicode, которые составляют пробел в JavaScript (за исключением кодовых точек Unicode-пробелов, которые я рассматриваю отдельно). Эти символы - горизонтальная табуляция, вертикальная табуляция, подача формы, пробел, неразрывный пробел и спецификация. Я мог бы сделать это с помощью магических чисел:

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

Это немного неясно; имена будут лучше. Метод unicodedata.lookup, переданный через ord, помогает некоторым:

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

Но это не работает для 0x9, 0xb или 0xc - я думаю, потому что они являются управляющими символами, а «имена» FORM FEED и так далее являются просто псевдонимами. Есть ли способ сопоставить эти «имена» с символами или их кодовыми точками в стандартном Python? Или мне не повезло?

Ответы [ 7 ]

13 голосов
/ 06 июля 2011

Комментарий Kerrek SB хорош: просто поместите имена в комментарии.

Кстати, Python также поддерживает именованный литерал юникода:

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

Но он использует ту же базу данных имен Unicode, а управляющих символов в ней нет.

2 голосов
/ 06 июля 2011

Я не думаю, что это можно сделать в стандартном Python. Модуль unicodedata использует UnicodeData.txt v5.2.0 базу данных Unicode. Обратите внимание, что всем управляющим символам присвоено имя <control> (второе поле, разделенное точкой с запятой).

Сценарий Tools/unicode/makeunicodedata.py в исходном дистрибутиве Python используется для генерации таблицы, используемой средой исполнения 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, которое вернет вам один из этих управляющих символов.

Просто жестко закодируйте кодовые точки для горизонтальной табуляции, перевода строки и возврата каретки и оставьте описательный комментарий. Как гласит Дзен Питона , «практичность побеждает чистоту».

2 голосов
/ 06 июля 2011

Вы можете свернуть свою собственную «базу данных» для управляющих символов, проанализировав несколько строк файлов UCD в общедоступном каталоге Unicode . В частности, смотрите файл UnicodeData-6.1.0d3 (или смотрите родительский каталог для более ранних версий).

1 голос
/ 07 июля 2011

Несколько баллов:

(1) «Спецификация» не является символом. Спецификация - это последовательность байтов, которая появляется в начале файла, чтобы указать порядок байтов в файле, который закодирован в UTF-nn. Спецификация - это u '\ uFEFF'.encode (' UTF-nn '). Чтение файла с соответствующим кодеком приведет к снижению производительности; Вы не видите это как символ Юникода. Спецификация - это не данные. Если вы видите в ваших данных u '\ uFEFF', рассматривайте его как (устарелое) НУКРЫТЫЙ ПРОБЕЛ ZEROWIDTH.

(2) «минус кодовые точки Unicode-пробел, которые я адресую отдельно» ?? Разве NO-BREAK SPACE не является кодовой точкой "Unicode-white-space"?

(3) Ваш Питон, кажется, сломан; мой делает это:

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

(4) Вы можете использовать escape-последовательности для первых трех.

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

(5) Вы можете использовать " " для четвертого.

(6) Даже если бы вы могли использовать имена, читатели вашего кода все равно использовали бы слепую веру, например, «ФОРМА ПЕРЕДАЧИ» - это пробельный символ.

(7) Что случилось с \r и \n?

0 голосов
/ 07 июля 2011

Вы можете расширить функцию поиска для обработки символов, которые не включены.

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'"
0 голосов
/ 06 июля 2011

Официальная рекомендация Unicode для новых строк может не совпадать с тем, как модуль Python codecs обрабатывает новые строки.Поскольку часто говорят, что u'\n' означает «новую строку», можно ожидать, основываясь на этой рекомендации, что строка Python u'\n' будет представлять символ U+2028 LINE SEPARATOR и кодироваться как таковой, а не как семантический управляющий символU+000A.Но я могу только представить себе путаницу, которая могла бы возникнуть, если бы модуль codecs действительно реализовал эту политику, и, кроме того, существуют допустимые контраргументы.То же самое относится к горизонтальной / вертикальной табуляции и подаче формы, которые, вероятно, в действительности не являются символами, но в любом случае являются элементами управления.(Я бы определенно думал, что backspace - это элемент управления, а не символ.)

Ваш вопрос, по-видимому, предполагает, что трактовка U+000A в качестве управляющего символа (вместо разделителя строк) неверна;но это совсем не точно.Возможно, для приложений обработки текста повсеместно ошибочнее считать, что устаревший управляющий сигнал прокрутки принтера - действительно настоящий «разделитель строк».

0 голосов
/ 06 июля 2011

Если вы работаете со строками 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_BE или codecs.BOM_UTF16_BE (хотя в Python 3+ она возвращается какbytes объект, а не str).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...