Содержание страницы
Руководство по Unicode¶Unicode HOWTO
- Версия
1.12
В этом HOWTO рассматривается поддержка Unicode в Python и объясняются различные проблемы, с которыми люди часто сталкиваются при работе с Unicode.
Введение в Unicode¶Introduction to Unicode
История кодировок символов¶History of Character Codes
В 1968 году был стандартизирован American Standard Code for Information Interchange, более известный под аббревиатурой ASCII. ASCII определил числовые коды для различных символов, причем числовые значения варьировались от 0 до 127. Например, строчная буква 'a' получила код 97.
ASCII был стандартом, разработанным в США, поэтому он определял только символы без диакритических знаков. Буква 'e' была, а 'é' или 'Í' – нет. Это означало, что языки, требующие диакритических знаков, не могли быть точно представлены в ASCII. (На самом деле отсутствие диакритических знаков имеет значение и для английского, в котором есть такие слова, как 'naïve' и 'café', а некоторые издательства требуют написания вроде 'coöperate'.)
Некоторое время люди просто писали программы, которые не отображали диакритические знаки. В середине 1980-х программа на Apple II BASIC, написанная носителем французского языка, могла содержать строки вроде этих:
PRINT "MISE A JOUR TERMINEE"
PRINT "PARAMETRES ENREGISTRES"
Эти сообщения должны содержать диакритические знаки (terminée, paramètre, enregistrés) и они выглядят неправильно для того, кто понимает французский.
В 1980-х почти все персональные компьютеры были 8-битными, то есть байты могли хранить значения от 0 до 255. Коды ASCII доходили только до 127, поэтому некоторые машины назначали значения от 128 до 255 символам с диакритическими знаками. Однако разные машины имели разные коды, что приводило к проблемам при обмене файлами. Со временем появились различные широко используемые наборы значений для диапазона 128–255. Некоторые были настоящими стандартами, определёнными Международной организацией по стандартизации, а некоторые были де-факто соглашениями, придуманными той или иной компанией и получившими распространение.
255 символов – это не так много. Например, невозможно вместить и диакритические символы, используемые в Западной Европе, и кириллицу, используемую для русского языка, в диапазон 128–255, потому что таких символов больше 128.
Можно было записывать файлы, используя разные кодировки (все русские файлы в кодировке KOI8, все французские файлы в другой кодировке Latin1), но что, если нужно было написать французский документ, цитирующий русский текст? В 1980-х люди начали искать решение этой проблемы, и началась работа по стандартизации Unicode.
Unicode изначально использовал 16-битные символы вместо 8-битных. 16 бит дают
2^16 = 65 536 различных значений, что позволяет представить множество различных символов
из разных алфавитов; изначальной целью было включить в Unicode алфавиты всех человеческих
языков. Оказалось, что даже 16 бит недостаточно для достижения этой цели, и современная
спецификация Unicode использует более широкий диапазон кодов – от 0 до 1 114 111 (
0x10FFFF в шестнадцатеричной системе).
Существует связанный стандарт ISO – ISO 10646. Unicode и ISO 10646 изначально были разными проектами, но спецификации были объединены в версии Unicode 1.1.
(Это обсуждение истории Unicode сильно упрощено. Точные исторические детали не обязательны для понимания того, как эффективно использовать Unicode, но если вам интересно, обратитесь к сайту консорциума Unicode, указанному в разделе «Ссылки», или к статье в Википедии об Unicode для получения дополнительной информации.)
Определения¶Definitions
Символ – это наименьший возможный компонент текста. 'A', 'B', 'C' и т.д. – всё это разные символы. Также как 'È' и 'Í'. Символы – это абстракции, и они варьируются в зависимости от языка или контекста. Например, символ ома (Ω) обычно рисуется очень похоже на заглавную букву омега (Ω) в греческом алфавите (в некоторых шрифтах они могут даже совпадать), но это два разных символа с разными значениями.
Стандарт Unicode описывает, как символы представляются с помощью кодовых
точек. Кодовая точка – это целое число, обычно обозначаемое в шестнадцатеричной системе. В
стандарте кодовая точка записывается с использованием обозначения U+12CA для обозначения
символа со значением 0x12ca (4 810 в десятичной системе). Стандарт Unicode содержит
множество таблиц, перечисляющих символы и соответствующие им кодовые точки:
0061 'a'; LATIN SMALL LETTER A
0062 'b'; LATIN SMALL LETTER B
0063 'c'; LATIN SMALL LETTER C
...
007B '{'; LEFT CURLY BRACKET
Строго говоря, из этих определений следует, что бессмысленно говорить «это
символ U+12CA». U+12CA – это кодовая точка, которая представляет некоторый конкретный
символ; в данном случае она представляет символ «ETHIOPIC SYLLABLE WI». В
неформальном контексте это различие между кодовыми точками и символами иногда забывают.
Символ отображается на экране или на бумаге с помощью набора графических элементов, который называется глифом. Глиф для заглавной A, например, представляет собой две диагональные и одну горизонтальную черту, хотя точные детали зависят от используемого шрифта. Большинству кода на Python не нужно беспокоиться о глифах; определение правильного глифа для отображения обычно является задачей GUI-инструментария или рендерера шрифтов терминала.
Кодировки¶Encodings
Резюмируя предыдущий раздел: строка Unicode – это последовательность кодовых
точек, которые являются числами от 0 до 0x10FFFF (1 114 111 в десятичной системе). Эта
последовательность должна быть представлена в памяти в виде набора байтов (то есть значений
от 0 до 255). Правила преобразования строки Unicode
в последовательность байтов называются кодировкой.
Первая кодировка, которая может прийти в голову – это массив 32-битных целых чисел. В этом представлении строка «Python» будет выглядеть так:
P y t h o n
0x50 00 00 00 79 00 00 00 74 00 00 00 68 00 00 00 6f 00 00 00 6e 00 00 00
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Такое представление интуитивно понятно, но его использование порождает ряд проблем.
Оно не является переносимым: разные процессоры по-разному упорядочивают байты.
Оно крайне неэкономно расходует место. В большинстве текстов кодовые точки в основном меньше 127 или 255, поэтому значительная часть места занята
0x00байтами. Приведённая выше строка занимает 24 байта, тогда как в ASCII-представлении достаточно 6 байт. Увеличение расхода оперативной памяти не так критично (настольные компьютеры имеют гигабайты RAM, а строки редко бывают очень большими), однако четырёхкратное расширение при использовании дискового пространства и сетевой пропускной способности уже неприемлемо.Оно несовместимо с существующими C-функциями, такими как
strlen(), поэтому пришлось бы использовать новое семейство функций для работы с широкими строками.Многие интернет-стандарты определены в терминах текстовых данных и не могут обрабатывать содержимое с встроенными нулевыми байтами.
Обычно люди не используют эту кодировку, предпочитая другие кодировки, которые более эффективны и удобны. UTF-8, вероятно, самая распространённая поддерживаемая кодировка; она будет рассмотрена ниже.
Кодировки не обязаны обрабатывать все возможные символы Unicode, и большинство кодировок этого не делают. Правила преобразования строки Unicode в кодировку ASCII, например, просты; для каждой кодовой точки:
Если кодовая точка < 128, каждый байт равен значению кодовой точки.
Если кодовая точка равна 128 или больше, строка Unicode не может быть представлена в этой кодировке. (В этом случае Python возбуждает исключение
UnicodeEncodeError.)
Latin-1, также известная как ISO-8859-1, является аналогичной кодировкой. Кодовые точки Unicode 0–255 идентичны значениям Latin-1, поэтому преобразование в эту кодировку просто требует преобразования кодовых точек в байтовые значения; если встречается кодовая точка больше 255, строка не может быть закодирована в Latin-1.
Кодировки не обязательно должны быть простыми однозначными отображениями, как Latin-1. Рассмотрим EBCDIC от IBM, которая использовалась на мейнфреймах IBM. Значения букв не были в одном блоке: от 'a' до 'i' имели значения от 129 до 137, а от 'j' до 'r' – от 145 до 153. Если бы вы хотели использовать EBCDIC в качестве кодировки, вам, вероятно, пришлось бы использовать какую-нибудь таблицу поиска для выполнения преобразования, но это в значительной степени внутренняя деталь.
UTF-8 – одна из наиболее часто используемых кодировок. UTF расшифровывается как «Unicode Transformation Format», а цифра '8' означает, что в кодировке используются 8-битные числа. (Существуют также кодировки UTF-16 и UTF-32, но они используются реже, чем UTF-8.) UTF-8 использует следующие правила:
Если кодовая точка < 128, она представляется соответствующим значением байта.
Если кодовая точка >= 128, она преобразуется в последовательность из двух, трёх или четырёх байт, причём каждый байт находится в диапазоне от 128 до 255.
UTF-8 обладает несколькими удобными свойствами:
Она может обрабатывать любую кодовую точку Юникода.
Строка Unicode преобразуется в последовательность байтов, не содержащую встроенных нулевых байтов. Это позволяет избежать проблем с порядком байтов и означает, что строки UTF-8 могут обрабатываться функциями C, такими как
strcpy(), и передаваться по протоколам, которые не могут обрабатывать нулевые байты.Любая строка ASCII также является корректным текстом UTF-8.
UTF-8 довольно компактна; большинство часто используемых символов можно представить одним или двумя байтами.
Если байты повреждены или потеряны, можно определить начало следующей UTF-8-кодовой точки и восстановить синхронизацию. Кроме того, маловероятно, что случайные 8-битные данные будут выглядеть как корректный UTF-8.
Ссылки¶References
The Unicode Consortium site has character charts, a glossary, and PDF versions of the Unicode specification. Be prepared for some difficult reading. A chronology of the origin and development of Unicode is also available on the site.
To help understand the standard, Jukka Korpela has written an introductory guide to reading the Unicode character tables.
Another good introductory article was written by Joel Spolsky. If this introduction didn’t make things clear to you, you should try reading this alternate article before continuing.
Wikipedia entries are often helpful; see the entries for “character encoding” and UTF-8, for example.
Поддержка Юникода в Python¶Python’s Unicode Support
Теперь, когда вы освоили основы Юникода, давайте рассмотрим возможности Python по работе с ним.
Тип строки¶The String Type
Начиная с Python 3.0, в языке появился тип str, содержащий символы
Unicode, то есть любая строка, созданная с помощью "unicode rocks!", 'unicode
rocks!' или синтаксиса тройных кавычек, хранится как Unicode.
Кодировка исходного кода Python по умолчанию – UTF-8, поэтому можно просто включить символ Юникода в строковый литерал:
try:
with open('/tmp/input.txt', 'r') as f:
...
except OSError:
# Сообщение об ошибке 'File not found'.
print("Fichier non trouvé")
Можно использовать другую кодировку, отличную от UTF-8, поместив специально оформленный комментарий в первую или вторую строку исходного кода:
# -*- coding: <encoding name> -*-
Попутное замечание: Python 3 также поддерживает использование символов Юникода в идентификаторах:
répertoire = "/tmp/records.log"
with open(répertoire, "w") as f:
f.write("test\n")
Если вы не можете ввести нужный символ в редакторе или по какой-то причине хотите сохранить исходный код только в ASCII, можно использовать escape-последовательности в строковых литералах. (В зависимости от системы вместо u-escape может отображаться сама заглавная дельта.)
>>> "\N{GREEK CAPITAL LETTER DELTA}" # Использование имени символа
'\u0394'
>>> "\u0394" # Использование 16-битного шестнадцатеричного значения
'\u0394'
>>> "\U00000394" # Использование 32-битного шестнадцатеричного значения
'\u0394'
Кроме того, строку можно создать методом decode() объекта bytes. Этот метод принимает аргумент encoding (кодировка), например UTF-8, и опционально аргумент errors (реакция на ошибки).
Аргумент errors определяет реакцию, когда входную строку невозможно преобразовать согласно правилам кодировки. Допустимые значения этого аргумента: 'strict' (вызывает исключение UnicodeDecodeError), 'replace' (использует U+FFFD, REPLACEMENT CHARACTER), 'ignore' (просто исключает символ из результата Юникода) или 'backslashreplace' (вставляет escape-последовательность \xNN). Следующие примеры показывают различия:
>>> b'\x80abc'.decode("utf-8", "strict")
Traceback (most recent call last):
...
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0:
invalid start byte
>>> b'\x80abc'.decode("utf-8", "replace")
'\ufffdabc'
>>> b'\x80abc'.decode("utf-8", "backslashreplace")
'\\x80abc'
>>> b'\x80abc'.decode("utf-8", "ignore")
'abc'
Кодировки задаются строками, содержащими название кодировки. Python 3.2
поставляется примерно со 100 различными кодировками; список можно найти в справочнике по библиотеке Python в разделе
Стандартные кодировки. Некоторые кодировки имеют несколько названий; например,
'latin-1', 'iso_8859_1' и '8859’ – всё это синонимы
одной и той же кодировки.
Односимвольные строки Юникода можно также создать с помощью встроенной функции chr(). Она принимает целое число и возвращает строку Юникода длиной 1, содержащую соответствующую кодовую точку. Обратная операция – встроенная функция ord(), которая принимает односимвольную строку Юникода и возвращает значение кодовой точки:
>>> chr(57344)
'\ue000'
>>> ord('\ue000')
57344
Преобразование в байты¶Converting to Bytes
Противоположным методом для bytes.decode() является str.encode(),
который возвращает bytes представление строки Unicode, закодированное в
требуемой кодировке.
Параметр errors такой же, как и параметр метода
decode(), но поддерживает несколько дополнительных обработчиков. Помимо
'strict', 'ignore' и 'replace' (который в данном случае
вставляет знак вопроса вместо некодируемого символа), есть
также 'xmlcharrefreplace' (вставляет ссылку на символ XML),
backslashreplace (вставляет управляющую последовательность \uNNNN) и
namereplace (вставляет управляющую последовательность \N{...}).
В следующем примере показаны различные результаты:
>>> u = chr(40960) + 'abcd' + chr(1972)
>>> u.encode('utf-8')
b'\xea\x80\x80abcd\xde\xb4'
>>> u.encode('ascii')
Traceback (most recent call last):
...
UnicodeEncodeError: 'ascii' codec can't encode character '\ua000' in
position 0: ordinal not in range(128)
>>> u.encode('ascii', 'ignore')
b'abcd'
>>> u.encode('ascii', 'replace')
b'?abcd?'
>>> u.encode('ascii', 'xmlcharrefreplace')
b'ꀀabcd޴'
>>> u.encode('ascii', 'backslashreplace')
b'\\ua000abcd\\u07b4'
>>> u.encode('ascii', 'namereplace')
b'\\N{YI SYLLABLE IT}abcd\\u07b4'
Низкоуровневые функции для регистрации и доступа к доступным
кодировкам находятся в модуле codecs. Реализация новых
кодировок также требует понимания модуля codecs.
Однако функции кодирования и декодирования, возвращаемые этим модулем,
обычно слишком низкоуровневые для комфортной работы, а написание новых кодировок
является специализированной задачей, поэтому этот модуль не будет рассматриваться в данном HOWTO.
Литералы Unicode в исходном коде Python¶Unicode Literals in Python Source Code
В исходном коде Python конкретные кодовые точки Unicode можно записать с помощью
управляющей последовательности \u, за которой следуют четыре шестнадцатеричные цифры, задающие кодовую
точку. Управляющая последовательность \U аналогична, но ожидает восемь шестнадцатеричных цифр,
а не четыре:
>>> s = "a\xac\u1234\u20ac\U00008000"
... # ^^^^ двузначная шестнадцатеричная escape-последовательность
... # ^^^^^^ четырёхзначная Unicode escape-последовательность
... # ^^^^^^^^^^ восьмизначная Unicode escape-последовательность
>>> [ord(c) for c in s]
[97, 172, 4660, 8364, 32768]
Использование управляющих последовательностей для кодовых точек больше 127 вполне допустимо в небольших количествах,
но становится утомительным, если используется много символов с диакритическими знаками, как,
например, в программе с сообщениями на французском или другом языке, использующем диакритику. Также можно
собирать строки с помощью встроенной функции chr(), но это ещё более трудоёмко.
В идеале хотелось бы иметь возможность записывать литералы в естественной кодировке вашего языка. Тогда можно было бы редактировать исходный код Python в любимом редакторе, который будет отображать символы с диакритическими знаками естественно, а при выполнении программы будут использоваться правильные символы.
По умолчанию Python поддерживает запись исходного кода в UTF-8, но можно использовать почти любую кодировку, если объявить используемую кодировку. Это делается путём включения специального комментария в первую или вторую строку исходного файла:
#!/usr/bin/env python
# -*- coding: latin-1 -*-
u = 'abcdé'
print(ord(u[-1]))
Синтаксис вдохновлён нотацией Emacs для указания локальных переменных файла.
Emacs поддерживает множество различных переменных, но Python поддерживает только
‘coding’. Символы -*- указывают Emacs, что комментарий специальный;
они не имеют значения для Python, но являются соглашением. Python ищет
coding: name или coding=name в комментарии.
Если такой комментарий не включён, по умолчанию будет использоваться кодировка UTF-8, как уже упоминалось. См. также PEP 263 для получения дополнительной информации.
Свойства Unicode¶Unicode Properties
Спецификация Unicode включает базу данных информации о кодовых точках. Для каждой определённой кодовой точки информация включает имя символа, его категорию, числовое значение, если применимо (Unicode содержит символы, представляющие римские цифры и дроби, такие как одна треть и четыре пятых). Существуют также свойства, связанные с использованием кодовой точки в двунаправленном тексте и другие свойства, относящиеся к отображению.
Следующая программа выводит некоторую информацию о нескольких символах и печатает числовое значение одного конкретного символа:
import unicodedata
u = chr(233) + chr(0x0bf2) + chr(3972) + chr(6000) + chr(13231)
for i, c in enumerate(u):
print(i, '%04x' % ord(c), unicodedata.category(c), end=" ")
print(unicodedata.name(c))
# Получить числовое значение второго символа
print(unicodedata.numeric(u[1]))
При запуске выводится:
0 00e9 Ll LATIN SMALL LETTER E WITH ACUTE
1 0bf2 No TAMIL NUMBER ONE THOUSAND
2 0f84 Mn TIBETAN MARK HALANTA
3 1770 Lo TAGBANWA LETTER SA
4 33af So SQUARE RAD OVER S SQUARED
1000.0
Коды категорий – это сокращения, описывающие природу символа.
Они группируются в такие категории, как «Буква», «Число», «Знак препинания» или
«Символ», которые в свою очередь делятся на подкатегории. Взяв коды
из приведённого выше вывода, 'Ll' означает «Буква, строчная», 'No' означает
«Число, прочее», 'Mn' – «Знак, некомбинируемый», а 'So' – «Символ,
прочее». См.
раздел «Значения общих категорий» документации по базе данных символов Unicode для
списка кодов категорий.
Регулярные выражения Unicode¶Unicode Regular Expressions
Регулярные выражения, поддерживаемые модулем re, могут быть представлены
как в виде байтов, так и строк. Некоторые специальные последовательности символов, такие как
\d и \w, имеют разные значения в зависимости от того,
задан ли шаблон как байты или строка. Например,
\d будет соответствовать символам [0-9] в байтах, но
в строках будет соответствовать любому символу из категории 'Nd'.
Строка в этом примере содержит число 57, записанное как тайскими, так и арабскими цифрами:
import re
p = re.compile(r'\d+')
s = "Over \u0e55\u0e57 57 flavours"
m = p.search(s)
print(repr(m.group()))
При выполнении \d+ будет соответствовать тайским цифрам и выведет их
на печать. Если передать флаг re.ASCII функции
compile(), то \d+ будет соответствовать подстроке «57».
Аналогично, \w соответствует широкому спектру символов Unicode, но
только [a-zA-Z0-9_] в байтах или если указан re.ASCII,
а \s будет соответствовать либо пробельным символам Unicode, либо
[ \t\n\r\f\v].
Ссылки¶References
Некоторые хорошие альтернативные обсуждения поддержки Unicode в Python:
Обработка текстовых файлов в Python 3, автор Ник Коглан.
Pragmatic Unicode – презентация Неда Бэтчелдера на PyCon 2012.
Тип str описан в справочнике по библиотеке Python по адресу
Text Sequence Type – str.
Документация модуля unicodedata.
Документация модуля codecs.
Марк-Андре Лембург выступил с презентацией «Python and Unicode» (слайды в PDF) на
EuroPython 2002. Слайды дают отличный обзор архитектуры поддержки Unicode в Python
2 (где тип строки Unicode называется unicode, а литералы начинаются с u).
Чтение и запись данных Unicode¶Reading and Writing Unicode Data
После написания кода, работающего с данными Unicode, следующая проблема – ввод/вывод. Как передавать строки Unicode в программу и как преобразовывать Unicode в форму, подходящую для хранения или передачи?
Возможно, вам может не понадобиться ничего делать в зависимости от источников ввода и мест назначения вывода; следует проверить, поддерживают ли библиотеки, используемые в вашем приложении, Unicode нативно. Например, XML-парсеры часто возвращают данные в Unicode. Многие реляционные базы данных также поддерживают столбцы со значением Unicode и могут возвращать значения Unicode из SQL-запросов.
Данные Unicode обычно преобразуются в определённую кодировку перед записью на диск или отправкой через сокет. Можно сделать всю работу самостоятельно: открыть файл, прочитать из него 8-битный объект bytes и преобразовать байты с помощью bytes.decode(encoding). Однако ручной подход не рекомендуется.
Одна из проблем – многобайтовая природа кодировок; один символ Unicode может быть представлен несколькими байтами. Если читать файл произвольными фрагментами (например, по 1024 или 4096 байт), необходимо написать код обработки ошибок для ситуации, когда в конце фрагмента считывается только часть байтов, кодирующих один символ Unicode. Одним из решений было бы прочитать весь файл в память и затем выполнить декодирование, но это не позволяет работать с очень большими файлами; если нужно прочитать файл размером 2 ГБ, потребуется 2 ГБ ОЗУ. (На самом деле больше, поскольку хотя бы на мгновение в памяти должны находиться и закодированная строка, и её Unicode-версия.)
Решением было бы использование низкоуровневого интерфейса декодирования для перехвата случая
частичных последовательностей кодирования. Работа по его реализации уже выполнена
за вас: встроенная функция open() может вернуть объект, похожий на файл
который предполагает, что содержимое файла находится в указанной кодировке, и принимает Unicode
параметры для таких методов, как read() и
write(). Это работает через open(): encoding и
errors параметры, которые интерпретируются так же, как в str.encode()
и bytes.decode().
Чтение Unicode из файла, таким образом, просто:
with open('unicode.txt', encoding='utf-8') as f:
for line in f:
print(repr(line))
Также можно открывать файлы в режиме обновления, позволяющем как чтение, так и запись:
with open('test', encoding='utf-8', mode='w+') as f:
f.write('\u4500 blah blah blah\n')
f.seek(0)
print(repr(f.readline()[:1]))
Символ Unicode U+FEFF используется как метка порядка байтов (BOM) и часто записывается как первый символ файла для облегчения автоматического определения порядка байтов файла. Некоторые кодировки, такие как UTF-16, ожидают наличие BOM в начале файла; при использовании такой кодировки BOM автоматически записывается как первый символ и молча отбрасывается при чтении файла. Существуют варианты этих кодировок, например, ‘utf-16-le’ и ‘utf-16-be’ для little-endian и big-endian кодировок, которые задают определённый порядок байтов и не пропускают BOM.
В некоторых областях также принято использовать «BOM» в начале файлов, закодированных в UTF-8; это название вводит в заблуждение, поскольку UTF-8 не зависит от порядка байтов. Метка просто объявляет, что файл закодирован в UTF-8. Используйте кодировку 'utf-8-sig', чтобы автоматически пропускать метку, если она присутствует, при чтении таких файлов.
Имена файлов в Unicode¶Unicode filenames
Большинство часто используемых сегодня операционных систем поддерживают имена файлов, содержащие произвольные символы Unicode. Обычно это реализуется путём преобразования строки Unicode в некоторую кодировку, которая зависит от системы. Например, Mac OS X использует UTF-8, а Windows – настраиваемую кодировку; в Windows Python использует имя «mbcs» для обозначения текущей настроенной кодировки. В Unix-системах файловая система получает кодировку только если заданы переменные окружения LANG или LC_CTYPE; в противном случае используется кодировка UTF-8 по умолчанию.
Функция sys.getfilesystemencoding() возвращает кодировку для использования в текущей системе, на случай если потребуется выполнить кодирование вручную, но в этом нет особой необходимости. При открытии файла для чтения или записи обычно можно просто передать строку Unicode в качестве имени файла, и она будет автоматически преобразована в нужную кодировку:
filename = 'filename\u4500abc'
with open(filename, 'w') as f:
f.write('blah\n')
Функции модуля os, такие как os.stat(), также принимают имена файлов Unicode.
Функция os.listdir() возвращает имена файлов и ставит вопрос: возвращать ли их в виде Unicode или в виде байтов с закодированными версиями? os.listdir() делает и то, и другое в зависимости от того, передан ли путь к каталогу как байты или как строка Unicode. Если передать путь в виде строки Unicode, имена файлов декодируются с использованием кодировки файловой системы и возвращается список строк Unicode, а если передать путь как байты, имена файлов возвращаются как байты. Например, если по умолчанию используется кодировка UTF-8, выполнение следующей программы:
fn = 'filename\u4500abc'
f = open(fn, 'w')
f.close()
import os
print(os.listdir(b'.'))
print(os.listdir('.'))
выдаст следующий результат:
amk:~$ python t.py
[b'filename\xe4\x94\x80abc', ...]
['filename\u4500abc', ...]
Первый список содержит имена файлов в кодировке UTF-8, а второй список – версии в Unicode.
Обратите внимание: в большинстве случаев следует использовать API Unicode. API для байтов стоит использовать только в системах, где могут встретиться не декодируемые имена файлов, то есть в Unix-системах.
Советы по написанию программ с поддержкой Unicode¶Tips for Writing Unicode-aware Programs
В этом разделе приводятся некоторые рекомендации по написанию программного обеспечения, работающего с Unicode.
Самый важный совет:
Программное обеспечение должно работать внутри только со строками Unicode, декодируя входные данные как можно раньше и кодируя вывод только в конце.
При попытке написать функции обработки, которые принимают как строки Unicode, так и байтовые строки, программа окажется уязвима для ошибок везде, где комбинируются два разных типа строк. Автоматического кодирования или декодирования не происходит: если выполнить, например, str + bytes, будет возбуждено TypeError.
При использовании данных из веб-браузера или другого ненадёжного источника распространённый приём – проверка строки на недопустимые символы перед использованием в генерируемой командной строке или сохранением в базе данных. Если это делается, следует проверять декодированную строку, а не закодированные байтовые данные; некоторые кодировки могут обладать интересными свойствами, такими как отсутствие биективности или полной совместимости с ASCII. Это особенно важно, если входные данные также задают кодировку, так как злоумышленник может выбрать хитрый способ скрыть вредоносный текст в закодированном потоке байтов.
Преобразование между кодировками файлов¶Converting Between File Encodings
Класс StreamRecoder может прозрачно преобразовывать между кодировками, принимая поток, возвращающий данные в кодировке №1, и ведя себя как поток, возвращающий данные в кодировке №2.
Например, если есть входной файл f в кодировке Latin-1, его можно обернуть с помощью StreamRecoder, чтобы возвращать байты, закодированные в UTF-8:
new_f = codecs.StreamRecoder(f,
# кодер/декодер: используется read() для кодирования результатов и
# с помощью write() для декодирования своего ввода.
codecs.getencoder('utf-8'), codecs.getdecoder('utf-8'),
# читатель/писатель: используется для чтения и записи в поток.
codecs.getreader('latin-1'), codecs.getwriter('latin-1') )
Файлы в неизвестной кодировке¶Files in an Unknown Encoding
Что делать, если нужно внести изменения в файл, но кодировка файла неизвестна? Если известно, что кодировка совместима с ASCII и требуется только просмотреть или изменить части в ASCII, можно открыть файл с обработчиком ошибок surrogateescape:
with open(fname, 'r', encoding="ascii", errors="surrogateescape") as f:
data = f.read()
# внести изменения в строку 'data'
with open(fname + '.new', 'w',
encoding="ascii", errors="surrogateescape") as f:
f.write(data)
Обработчик ошибок surrogateescape декодирует любые не-ASCII байты как кодовые точки в области частного использования Unicode в диапазоне от U+DC80 до U+DCFF. Затем эти частные кодовые точки преобразуются обратно в те же байты, когда при кодировании данных и их записи обратно используется обработчик ошибок surrogateescape.
Ссылки¶References
Одна из секций доклада Mastering Python 3 Input/Output на PyCon 2010 (Дэвид Бизли) посвящена обработке текста и работе с двоичными данными.
В слайдах PDF презентации Марка-Андре Лембурга «Writing Unicode-aware Applications in Python» рассматриваются вопросы кодировок символов, а также интернационализация и локализация приложений. Эти слайды охватывают только Python 2.x.
The Guts of Unicode in Python – доклад Бенджамина Петерсона на PyCon 2013, в котором обсуждается внутреннее представление Unicode в Python 3.3.
Благодарности¶Acknowledgements
Первоначальный черновик этого документа написал Эндрю Кухлинг. Впоследствии он был доработан Александром Белопольским, Георгом Брандлем, Эндрю Кухлингом и Эцио Мелотти.
Благодарности следующим людям, которые заметили ошибки или предложили улучшения для этой статьи: Éric Araujo, Nicholas Bastin, Nick Coghlan, Marius Gedminas, Kent Johnson, Ken Krugler, Marc-André Lemburg, Martin von Löwis, Terry J. Reedy, Chad Whitacre.