Как работать с Unicode в Python
Автор выбрал программу Write for DOnations.
Введение
Юникод — это стандартная кодировка символов для большинства компьютеров в мире. Это гарантирует, что текст, включая буквы, символы, эмодзи и даже управляющие символы, будет выглядеть одинаково на разных устройствах, платформах и цифровых документах, независимо от используемой операционной системы или программного обеспечения. Это важная часть Интернета и компьютерной индустрии, и без нее Интернет был бы гораздо более хаотичным и сложным в использовании.
Юникод сам по себе не является кодировкой, а больше похож на базу данных почти всех возможных символов на земле. Unicode содержит кодовую точку, идентификатор для каждого символа в своей базе данных, который может иметь значение от 0 до 1,1 миллиона, а это означает, что маловероятно, что в ближайшее время у него закончатся эти уникальные кодовые точки. Каждая кодовая точка в Unicode представлена как U+n
, где U+
означает, что это кодовая точка Unicode, а n
— это набор из четырех до шести шестнадцатеричных цифр для символа. Это гораздо более надежная система кодирования, чем ASCII, которая представляет только 128 символов. Обмен цифровым текстом по всему миру с помощью ASCII был затруднен, потому что он основан на американском английском и не поддерживает символы с диакритическими знаками. Unicode, с другой стороны, содержит почти 150 000 символов и охватывает символы всех языков мира.
С этим связано требование к языкам программирования, таким как Python, правильно обрабатывать текст и обеспечивать интернационализацию программного обеспечения. Python можно использовать для самых разных целей — от электронной почты до серверов и Интернета — и у него есть элегантный способ обработки Unicode, который он делает, принимая стандарт Unicode для своих строк.
Однако работа с Unicode в Python может сбивать с толку и приводить к ошибкам. В этом руководстве представлены основы использования Unicode в Python, которые помогут вам избежать этих проблем. Вы будете использовать Python для интерпретации Unicode, нормализации данных с помощью функций нормализации Python и обработки ошибок Python Unicode.
Предпосылки
Чтобы следовать этому руководству, вам понадобятся:
- Python установлен локально или на удаленном сервере. Если у вас еще не установлен Python, вы можете сделать это, следуя нашему руководству «Как установить Python 3 и настроить среду программирования». Выберите версию, подходящую для вашего дистрибутива Linux.
- Знакомство с основами программирования Python и строковыми методами Python
- Знание того, как работать с интерактивной консолью Python.
Шаг 1 — Преобразование кодовых точек Unicode в Python
Кодирование — это процесс представления данных в машиночитаемой форме. Существует множество способов кодирования данных — ASCII, Latin-1 и другие, и каждая кодировка имеет свои сильные и слабые стороны, но, пожалуй, наиболее распространенной является UTF-8. Это тип кодирования, который позволяет отображать символы со всего мира в одном наборе символов. Таким образом, UTF-8 является важным инструментом для всех, кто работает с интернационализированными данными. В общем, UTF-8 — хороший выбор для большинства целей. Он относительно эффективен и может использоваться с различным программным обеспечением. UTF-8 берет кодовую точку Unicode и преобразует ее в шестнадцатеричные байты, понятные компьютеру. Другими словами, Unicode — это сопоставление, а UTF-8 позволяет компьютеру понять это сопоставление.
В Python 3 кодировкой строки по умолчанию является UTF-8, что означает, что любая кодовая точка Unicode в строке Python автоматически преобразуется в соответствующий символ.
На этом шаге вы создадите символ авторского права (©), используя его кодовую точку Unicode в Python. Сначала запустите интерактивную консоль Python в своем терминале и введите следующее:
>>> s = '\u00A9'
>>> s
В предыдущем коде вы создали строку s
с кодовой точкой Unicode \u00A9
. Как упоминалось ранее, поскольку строка Python по умолчанию использует кодировку UTF-8, печать значения s
автоматически заменяет его на соответствующий символ Unicode. Обратите внимание, что \u
в начале кода обязателен. Без него Python не сможет преобразовать кодовую точку. Вывод предыдущего кода возвращает соответствующий символ Unicode:
Output'©'
Язык программирования Python предоставляет встроенные функции для кодирования и декодирования строк. Функция encode()
преобразует строку в строку байтов.
Чтобы продемонстрировать это, откройте интерактивную консоль Python и введите следующий код:
>>> '🅥'.encode('utf-8')
Это создает строку байтов символа в качестве вывода:
Outputb'\xf0\x9f\x85\xa5'
Обратите внимание, что перед каждым байтом стоит \x
, что указывает на то, что это шестнадцатеричное число.
Примечание. Ввод специальных символов Юникода в Windows и на Mac отличается. В предыдущем коде и во всем коде в этом руководстве, где используются символы, вы можете вставлять символы с помощью служебной программы «Карта символов» в Windows. У Mac нет этой функции, поэтому лучше всего скопировать символ из примеров кода.
Затем вы будете использовать функцию decode()
для преобразования строки байтов обратно в строку. Функция decode()
принимает тип кодировки в качестве аргумента. Также стоит отметить, что функция decode()
может декодировать только строку байтов, которая указывается буквой b
в начале строки. Удаление b
приведет к ошибке AttributeError
.
В консоли введите:
>>> b'\xf0\x9f\x85\xa5'.decode('utf-8')
Код вернет вывод следующим образом:
Output'🅥'
Теперь у вас есть фундаментальное понимание интерпретации Unicode в Python. Далее вы погрузитесь во встроенный в Python модуль unicodedata
, чтобы использовать расширенные методы Unicode для ваших строк.
Шаг 2 — Нормализация Unicode в Python
На этом шаге вы нормализуете Unicode в Python. Нормализация помогает определить, являются ли два символа, написанные разными шрифтами, одинаковыми, что полезно, когда два символа с разными кодовыми точками дают одинаковый результат. Например, символы Unicode R
и ℜ
для человеческого глаза одинаковы, так как они оба представляют собой букву R
, но компьютер считает чтобы они были разными персонажами.
Следующий пример кода дополнительно демонстрирует это. Откройте консоль Python и введите следующее:
>>> styled_R = 'ℜ'
>>> normal_R = 'R'
>>> styled_R == normal_R
Вы получите следующий вывод:
OutputFalse
Код выводит False
в качестве вывода, потому что строки Python не считают эти два символа идентичными. Именно благодаря этой способности различать нормализация важна при работе с Unicode.
В Unicode некоторые символы создаются путем объединения двух или более символов в один. В этом случае важна нормализация, потому что она обеспечивает согласованность строк друг с другом. Чтобы лучше понять это, откройте консоль Python и введите следующий код:
>>> s1 = 'hôtel'
>>> s2 = 'ho\u0302tel'
>>> len(s1), len(s2)
В предыдущем коде вы создали строку s1
, содержащую символ ô
, а во второй строке строка s2
содержит кодовую точку символ циркумфлекса ( ̂ ). После выполнения код возвращает следующий вывод:
Output(5, 6)
Предыдущий вывод показывает, что две строки состоят из одних и тех же символов, но имеют разную длину, что означает, что они не будут равными. Введите следующее в той же консоли, чтобы проверить это:
>>> s1 == s2
Код возвращает следующий вывод:
OutputFalse
Хотя строковые переменные s1
и s2
создают один и тот же символ Unicode, они различаются по длине и, следовательно, не равны.
Вы можете решить эту проблему с помощью функции normalize()
, что вы и сделаете на следующем шаге.
Шаг 3 — Нормализация Unicode с помощью NFD, NFC, NFKD и NFKC
На этом шаге вы нормализуете строки Unicode с помощью функции normalize()
из библиотеки unicodedata Python в модуле unicodedata
, который обеспечивает возможности поиска и нормализации символов. Функция normalize()
может принимать форму нормализации в качестве первого аргумента и нормализуемую строку в качестве второго аргумента. В Unicode есть четыре типа форм нормализации, которые вы можете использовать для этого: NFD, NFC, NFKD и NFKC.
Форма нормализации NFD разбивает символ на несколько комбинируемых символов. Это делает ваш текст нечувствительным к акценту, что может быть полезно при поиске и сортировке. Вы можете сделать это, закодировав строку в байты.
Откройте консоль и введите следующее:
>>> from unicodedata import normalize
>>> s1 = 'hôtel'
>>> s2 = 'ho\u0302tel'
>>> s1_nfd = normalize('NFD', s1)
>>> len(s1), len(s1_nfd)
Код производит следующий вывод:
Output(5, 6)
Как показывает пример, нормализация строки s1
увеличивает ее длину на один символ. Это связано с тем, что символ ô
разделяется на два символа, o
и ˆ
, что вы можете подтвердить, используя следующий код:
>>> s1.encode(), s1_nfd.encode()
Полученный вывод показывает, что после кодирования нормализованной строки символ o
отделился от символа ˆ
в строке s1_nfd
:
Output(b'h\xc3\xb4tel', b'ho\xcc\x82tel')
Форма нормализации NFC сначала разлагает символ, а затем повторно компонует его с любым доступным комбинирующим символом. Поскольку NFC составляет строку для получения кратчайшего результата, W3C рекомендует использовать NFC в Интернете. Ввод с клавиатуры по умолчанию возвращает составленные строки, поэтому в этом случае рекомендуется использовать NFC.
Например, введите в интерактивную консоль следующее:
>>> from unicodedata import normalize
>>> s2_nfc = normalize('NFC', s2)
>>> len(s2), len(s2_nfc)
Код производит следующий вывод:
Output(6, 5)
В примере нормализация строки s2
уменьшает ее длину на единицу. Вы можете убедиться в этом, запустив следующий код в интерактивной консоли:
>>> s2.encode(), s2_nfc.encode()
Вывод кода:
Output(b'ho\xcc\x82tel', b'h\xc3\xb4tel')
Вывод показывает, что символы o
и ˆ
объединены в один символ ô
.
Формы нормализации NFKD и NFKC используются для «строгой» нормализации и могут использоваться для решения множества задач, связанных с поиском и сопоставлением с образцом в строках Unicode. «K» в NFKD и NFKC означает совместимость.
Формы нормализации NFD и NFC разлагают символы, но NFKD и NFKC выполняют разложение совместимости для символов, которые не похожи, но эквивалентны, удаляя любые различия форматирования. Например, строка ②①
не похожа на 21
, но обе они представляют одно и то же значение. Формы нормализации NFKC и NFKD удаляют это форматирование (в данном случае круг вокруг цифр) из символов, чтобы обеспечить их наиболее упрощенную форму.
Следующий пример демонстрирует разницу между формами нормализации NFD и NFKD. Откройте интерактивную консоль Python и введите следующее:
>>> s1 = '2⁵ô'
>>> from unicodedata import normalize
>>> normalize('NFD', s1), normalize('NFKD', s1)
Вы получите следующий вывод:
Output('2⁵ô', '25ô')
Вывод показывает, что форма NFD не может разложить символ экспоненты в строке s1
, но NFKD вырезает форматирование экспоненты и заменяет символ совместимости (в данном случае экспоненту 5
) с его эквивалентом (5
как цифра). Помните, что формы нормализации NFD и NFKD все еще разлагают символы, поэтому символ ô
должен увеличить свою длину на единицу, как вы видели в предыдущем примере NFD. Вы можете подтвердить это, выполнив следующий код:
>>> len(normalize('NFD', s1)), len(normalize('NFKD', s1))
Код вернет следующее:
Output(4, 4)
Форма нормализации NFKC работает аналогичным образом, но создает символы, а не разлагает их. В той же консоли Python введите следующее:
>>> normalize('NFC', s1), normalize('NFKC', s1)
Код возвращает следующее:
Output('2⁵ô', '25ô')
Поскольку NFKC придерживается композиционного подхода, вы должны ожидать, что строка для символа ô
будет сокращена на единицу, а не удлиняться на единицу в случае декомпозиции. Вы можете убедиться в этом, запустив следующую строку кода:
>>> len(normalize('NFC', s1)), len(normalize('NFKC', s1))
Это вернет следующий вывод:
Output(3, 3)
Выполнив предыдущие шаги, вы получите практические знания о типах форм нормализации и различиях между ними. На следующем шаге вы будете решать ошибки Unicode в Python.
Шаг 4 — Решение ошибок Unicode в Python
При обработке Unicode в Python могут возникать два типа ошибок Unicode: UnicodeEncodeError и UnicodeDecodeError. Хотя эти ошибки Unicode могут сбивать с толку, ими можно управлять, и вы исправите обе эти ошибки на этом шаге.
Решение ошибки UnicodeEncodeError
Кодирование в Unicode — это процесс преобразования строки Unicode в байты с использованием определенной кодировки. Ошибка UnicodeEncodeError возникает при попытке закодировать строку, содержащую символы, которые не могут быть представлены в указанной кодировке.
Чтобы создать эту ошибку, вы будете кодировать строку, содержащую символы, не входящие в набор символов ASCII.
Откройте консоль и введите следующее:
>>> ascii_supported = '\u0041'
>>> ascii_supported.encode('ascii')
Ниже приведен ваш вывод:
Outputb'A'
Затем введите следующее:
>>> ascii_unsupported = '\ufb06'
>>> ascii_unsupported.encode('utf-8')
Вы получите следующий результат:
Outputb'\xef\xac\x86'
Наконец, введите следующее:
>>> ascii_unsupported.encode('ascii')
Однако при запуске этого кода вы получите следующую ошибку:
OutputTraceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character '\ufb06' in position 0: ordinal not in range(128)
ASCII имеет ограниченное количество символов, и Python выдает ошибки, когда находит символ, которого нет в кодировке ASCII. Поскольку кодировка ASCII не распознает кодовую точку \ufb06
, Python возвращает сообщение об ошибке, в котором говорится, что ASCII имеет диапазон только 128 и соответствующий десятичный эквивалент этой кодовой точки не находится в этом диапазоне.
Вы можете обработать UnicodeEncodeError, используя аргумент errors
в функции encode()
. Аргумент errors
может иметь одно из трех значений: ignore
, replace
и xmlcharrefreplace
.
Откройте консоль и введите следующее:
>>> ascii_unsupported = '\ufb06'
>>> ascii_unsupported.encode('ascii', errors='ignore')
Вы получите следующий вывод
Outputb''
Далее введите следующее:
>>> >>> ascii_unsupported.encode('ascii', errors='replace')
Вывод будет:
Outputb'?'
Наконец, введите следующее:
>>> ascii_unsupported.encode('ascii', errors='xmlcharrefreplace')
Результат:
Outputb'st'
В каждом случае Python не выдает ошибку.
Как показано в предыдущем примере, ignore
пропускает символ, который не может быть закодирован, replace
заменяет символ на ?
, а xmlcharrefreplace
заменяет некодируемые символы сущностью XML.
Решение ошибки UnicodeDecodeError
UnicodeDecodeError возникает при попытке декодировать строку, содержащую символы, которые не могут быть представлены в указанной кодировке.
Чтобы создать эту ошибку, вы попытаетесь декодировать строку байтов в кодировку, которую невозможно декодировать.
Откройте консоль и введите следующее:
>>> iso_supported = '§'
>>> b = iso_supported.encode('iso8859_1')
>>> b.decode('utf-8')
Вы получите следующую ошибку:
OutputTraceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa7 in position 0: invalid start byte
Если вы столкнулись с этой ошибкой, вы можете использовать аргумент errors
в функции decode()
, что может помочь вам декодировать строку. Аргумент errors
принимает два значения: ignore
и replace
.
Чтобы продемонстрировать это, откройте консоль Python и введите следующий код:
>>> iso_supported = '§A'
>>> b = iso_supported.encode('iso8859_1')
>>> b.decode('utf-8', errors='replace')
Ваш вывод будет:
Output'�A'
Затем введите следующее:
>>> b.decode('utf-8', errors='ignore')
Ваш вывод будет:
Output'A'
В предыдущем примере при использовании значения replace
в функции decode()
был добавлен символ �
, а при использовании ignore
ничего не возвращал, когда декодер (в данном случае utf-8
) не мог декодировать байты.
При декодировании любой строки обратите внимание, что вы не можете предположить, что это за кодировка. Чтобы расшифровать любую строку, вы должны знать, как она была закодирована.
Заключение
В этой статье были рассмотрены основы использования Unicode в Python. Вы кодировали и декодировали строки, нормализовали данные с помощью NFD, NFC, NFKD и NFKC и устраняли ошибки Unicode. Вы также использовали формы нормализации в сценариях, связанных с сортировкой и поиском. Эти методы помогут вам справиться с проблемами Unicode с помощью Python. В качестве следующего шага вы можете прочитать How To Code in Python 3.