Документация Python неофициальный перевод

unicode.md

446 строк · 52.1 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/2.7/howto/unicode.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Руководство по Unicode89**Версия**10111.031213В этом HOWTO рассматривается поддержка Unicode в Python 2.x и объясняются различные проблемы, с которыми обычно сталкиваются при работе с Unicode. Версию для Python 3 см. на странице \<[https://docs.python.org/3/howto/unicode.html](https://python-all.ru/3/howto/unicode.html)\>.1415## Введение в Unicode1617### История кодировок символов1819В 1968 году был стандартизирован американский стандартный код для обмена информацией, более известный под аббревиатурой ASCII. ASCII определил числовые коды для различных символов, значения которых находятся в диапазоне от 0 до 127. Например, строчной букве «a» присвоено значение кода 97.2021ASCII был стандартом, разработанным в США, поэтому он определял только символы без диакритических знаков. Буква 'e' была, а 'é' или 'Í' – нет. Это означало, что языки, требующие диакритических знаков, не могли быть точно представлены в ASCII. (На самом деле отсутствие диакритических знаков имеет значение и для английского, в котором есть такие слова, как 'naïve' и 'café', а некоторые издательства требуют написания вроде 'coöperate'.)2223Некоторое время люди просто писали программы, которые не отображали диакритические знаки. Помню, как смотрел на программы на Apple \]\[ BASIC, опубликованные во франкоязычных изданиях в середине 1980-х, в которых были такие строки:2425```python26PRINT "MISE A JOUR TERMINEE"27PRINT "PARAMETRES ENREGISTRES"28```2930Эти сообщения должны содержать диакритические знаки, и они выглядят неправильно для того, кто умеет читать по-французски.3132В 1980-х почти все персональные компьютеры были 8-битными, то есть байты могли хранить значения от 0 до 255. Коды ASCII доходили только до 127, поэтому некоторые машины назначали значения от 128 до 255 символам с диакритическими знаками. Однако разные машины имели разные коды, что приводило к проблемам при обмене файлами. Со временем появились различные широко используемые наборы значений для диапазона 128–255. Некоторые были настоящими стандартами, определёнными Международной организацией по стандартизации, а некоторые были *де-факто* соглашениями, придуманными той или иной компанией и получившими распространение.3334255 символов – это не так много. Например, невозможно вместить и диакритические символы, используемые в Западной Европе, и кириллицу, используемую для русского языка, в диапазон 128–255, потому что таких символов больше 128.3536Можно было записывать файлы, используя разные кодировки (все русские файлы в кодировке KOI8, все французские файлы в другой кодировке Latin1), но что, если нужно было написать французский документ, цитирующий русский текст? В 1980-х люди начали искать решение этой проблемы, и началась работа по стандартизации Unicode.3738Изначально Unicode использовал 16-битные символы вместо 8-битных. 16 бит дают 2^16 = 65 536 различных значений, что позволяет представлять множество символов из разных алфавитов; первоначальной целью было включить в Unicode алфавиты всех человеческих языков. Оказалось, что даже 16 бит недостаточно для достижения этой цели, и современная спецификация Unicode использует более широкий диапазон кодов: от 0 до 1 114 111 (0x10ffff в шестнадцатеричной системе).3940Существует связанный стандарт ISO – ISO 10646. Unicode и ISO 10646 изначально были разными проектами, но спецификации были объединены в версии Unicode 1.1.4142(Это обсуждение истории Unicode сильно упрощено. Не думаю, что среднестатистическому программисту на Python нужно беспокоиться об исторических деталях; обращайтесь к сайту консорциума Unicode, указанному в разделе «Ссылки», за дополнительной информацией.)4344### Определения4546**Символ** – это наименьший возможный компонент текста. 'A', 'B', 'C' и т.д. – всё это разные символы. Также как 'È' и 'Í'. Символы – это абстракции, и они варьируются в зависимости от языка или контекста. Например, символ ома (Ω) обычно рисуется очень похоже на заглавную букву омега (Ω) в греческом алфавите (в некоторых шрифтах они могут даже совпадать), но это два разных символа с разными значениями.4748Стандарт Unicode описывает, как символы представлены с помощью **кодовых точек**. Кодовая точка – это целое число, обычно записываемое в шестнадцатеричной системе. В стандарте кодовая точка записывается в виде U+12ca и означает символ со значением 0x12ca (4810 в десятичной системе). Стандарт Unicode содержит множество таблиц, перечисляющих символы и соответствующие им кодовые точки:4950```python510061    'a'; LATIN SMALL LETTER A520062    'b'; LATIN SMALL LETTER B530063    'c'; LATIN SMALL LETTER C54...55007B    '{'; LEFT CURLY BRACKET56```5758Строго говоря, из этих определений следует, что бессмысленно говорить «это символ U+12ca». U+12ca – это кодовая точка, которая представляет некоторый конкретный символ; в данном случае она представляет символ «ETHIOPIC SYLLABLE WI». В неформальном контексте это различие между кодовыми точками и символами иногда забывается.5960Символ отображается на экране или на бумаге с помощью набора графических элементов, который называется **глифом**. Глиф для заглавной A, например, представляет собой две диагональные и одну горизонтальную черту, хотя точные детали зависят от используемого шрифта. Большинству кода на Python не нужно беспокоиться о глифах; определение правильного глифа для отображения обычно является задачей GUI-инструментария или рендерера шрифтов терминала.6162### Кодировки6364Резюмируя предыдущий раздел: строка Unicode – это последовательность кодовых точек, представляющих собой числа от 0 до 0x10ffff. Эта последовательность должна быть представлена в памяти в виде набора байтов (то есть значений от 0 до 255). Правила преобразования строки Unicode в последовательность байтов называются **кодировкой**.6566Первая кодировка, которая может прийти в голову – это массив 32-битных целых чисел. В этом представлении строка «Python» будет выглядеть так:6768```python69   P           y           t           h           o           n700x50 00 00 00 79 00 00 00 74 00 00 00 68 00 00 00 6f 00 00 00 6e 00 00 0071   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 2372```7374Такое представление интуитивно понятно, но его использование порождает ряд проблем.75761. Оно не является переносимым: разные процессоры по-разному упорядочивают байты.772. Это очень расточительно по памяти. В большинстве текстов большинство кодовых точек меньше 127 или меньше 255, поэтому много места занято нулевыми байтами. Приведенная выше строка занимает 24 байта по сравнению с 6 байтами, необходимыми для ASCII-представления. Увеличение использования оперативной памяти не так важно (настольные компьютеры имеют мегабайты ОЗУ, и строки обычно не такие большие), но увеличение использования дискового пространства и пропускной способности сети в 4 раза недопустимо.783. Оно несовместимо с существующими C-функциями, такими как `strlen()`, поэтому пришлось бы использовать новое семейство функций для работы с широкими строками.794. Многие интернет-стандарты определены в терминах текстовых данных и не могут обрабатывать содержимое с встроенными нулевыми байтами.8081Обычно люди не используют эту кодировку, предпочитая другие кодировки, которые более эффективны и удобны. UTF-8, вероятно, самая распространённая поддерживаемая кодировка; она будет рассмотрена ниже.8283Кодировки не обязаны поддерживать все возможные символы Unicode, и большинство из них этого не делает. Например, кодировка по умолчанию в Python – это 'ascii'. Правила преобразования строки Unicode в кодировку ASCII просты: для каждой кодовой точки:84851. Если кодовая точка \< 128, каждый байт равен значению кодовой точки.862. Если кодовая точка равна 128 или больше, строка Unicode не может быть представлена в этой кодировке. (В этом случае Python возбуждает исключение [`UnicodeEncodeError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.UnicodeEncodeError).)8788Latin-1, также известная как ISO-8859-1, является аналогичной кодировкой. Кодовые точки Unicode 0–255 идентичны значениям Latin-1, поэтому преобразование в эту кодировку просто требует преобразования кодовых точек в байтовые значения; если встречается кодовая точка больше 255, строка не может быть закодирована в Latin-1.8990Кодировки не обязательно должны быть простыми однозначными отображениями, как Latin-1. Рассмотрим EBCDIC от IBM, которая использовалась на мейнфреймах IBM. Значения букв не были в одном блоке: от 'a' до 'i' имели значения от 129 до 137, а от 'j' до 'r' – от 145 до 153. Если бы вы хотели использовать EBCDIC в качестве кодировки, вам, вероятно, пришлось бы использовать какую-нибудь таблицу поиска для выполнения преобразования, но это в значительной степени внутренняя деталь.9192UTF-8 – одна из наиболее часто используемых кодировок. UTF расшифровывается как «Unicode Transformation Format», а «8» означает, что в кодировке используются 8-битные числа. (Существует также кодировка UTF-16, но она используется реже, чем UTF-8.) UTF-8 использует следующие правила:93941. Если кодовая точка меньше 128, она представляется соответствующим значением байта.952. Если кодовая точка находится между 128 и 0x7ff, она преобразуется в два значения байтов от 128 до 255.963. Кодовые точки больше 0x7ff преобразуются в последовательности из трёх или четырёх байтов, где каждый байт последовательности находится в диапазоне от 128 до 255.9798UTF-8 обладает несколькими удобными свойствами:991001. Она может обрабатывать любую кодовую точку Юникода.1012. Строка Unicode преобразуется в строку байтов, не содержащую встроенных нулевых байтов. Это позволяет избежать проблем с порядком байтов и даёт возможность обрабатывать строки UTF-8 такими функциями C, как `strcpy()`, и передавать их по протоколам, которые не поддерживают нулевые байты.1023. Любая строка ASCII также является корректным текстом UTF-8.1034. UTF-8 довольно компактна; большинство кодовых точек преобразуются в два байта, а значения меньше 128 занимают только один байт.1045. Если байты повреждены или потеряны, можно определить начало следующей UTF-8-кодовой точки и восстановить синхронизацию. Кроме того, маловероятно, что случайные 8-битные данные будут выглядеть как корректный UTF-8.105106### Ссылки107108На сайте Unicode Consortium по адресу \<[http://www.unicode.org](https://python-all.ru/2.7/howto/unicode.html)\> есть таблицы символов, глоссарий и PDF-версии спецификации Unicode. Будьте готовы к сложному чтению. \<[http://www.unicode.org/history/](https://python-all.ru/2.7/howto/unicode.html)\> – это хронология происхождения и развития Unicode.109110В помощь изучающим стандарт Юкка Корпела написал вводное руководство по чтению таблиц символов Unicode, доступное по адресу \<[https://www.cs.tut.fi/~jkorpela/unicode/guide.html](https://python-all.ru/2.7/howto/unicode.html)\>.111112Ещё одну хорошую вводную статью написал Джоэл Спольски \<[http://www.joelonsoftware.com/articles/Unicode.html](https://python-all.ru/2.7/howto/unicode.html)\>. Если это введение не прояснило для вас ситуацию, попробуйте прочитать эту альтернативную статью, прежде чем продолжать.113114Статьи в Википедии часто полезны; см. статьи «character encoding» \<[http://en.wikipedia.org/wiki/Character\_encoding](https://python-all.ru/2.7/howto/unicode.html)\> и UTF-8 \<[http://en.wikipedia.org/wiki/UTF-8](https://python-all.ru/2.7/howto/unicode.html)\>, например.115116## Поддержка Unicode в Python 2.x117118Теперь, когда вы освоили основы Юникода, давайте рассмотрим возможности Python по работе с ним.119120### Тип Unicode121122Строки Unicode представлены экземплярами типа [`unicode`](https://python-all.ru/2.7/library/functions.html#unicode), одного из встроенных типов Python. Он происходит из абстрактного типа [`basestring`](https://python-all.ru/2.7/library/functions.html#basestring), который также является предком типа [`str`](https://python-all.ru/2.7/library/functions.html#str); поэтому проверить, является ли значение строковым типом, можно с помощью `isinstance(value, basestring)`. Внутри Python представляет строки Unicode как 16- или 32-битные целые числа в зависимости от того, как был скомпилирован интерпретатор Python.123124Конструктор [`unicode()`](https://python-all.ru/2.7/library/functions.html#unicode) имеет сигнатуру `unicode(string[, encoding, errors])`. Все его аргументы должны быть 8-битными строками. Первый аргумент преобразуется в Unicode с использованием указанной кодировки; если опустить аргумент `encoding`, для преобразования используется кодировка ASCII, поэтому символы больше 127 будут трактоваться как ошибки:125126```python127>>> unicode('abcdef')128u'abcdef'129>>> s = unicode('abcdef')130>>> type(s)131<type 'unicode'>132>>> unicode('abcdef' + chr(255))    133Traceback (most recent call last):134...135UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 6:136ordinal not in range(128)137```138139Аргумент `errors` определяет реакцию, когда входную строку невозможно преобразовать по правилам кодировки. Допустимые значения этого аргумента: ‘strict’ (вызвать исключение `UnicodeDecodeError`), ‘replace’ (добавить U+FFFD, ‘REPLACEMENT CHARACTER’) или ‘ignore’ (просто исключить символ из результата Unicode). Следующие примеры показывают различия:140141```python142>>> unicode('\x80abc', errors='strict')     143Traceback (most recent call last):144    ...145UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 in position 0:146ordinal not in range(128)147>>> unicode('\x80abc', errors='replace')148u'\ufffdabc'149>>> unicode('\x80abc', errors='ignore')150u'abc'151```152153Кодировки задаются строками, содержащими их имена. Python 2.7 поставляется примерно со 100 различными кодировками; полный список см. в справочнике по библиотеке Python в разделе [Стандартные кодировки](https://python-all.ru/2.7/library/codecs.html#standard-encodings). Некоторые кодировки имеют несколько имен; например, ‘latin-1’, ‘iso\_8859\_1’ и ‘8859’ – это синонимы одной и той же кодировки.154155Односимвольные строки Юникода можно также создать с помощью встроенной функции [`unichr()`](https://python-all.ru/2.7/library/functions.html#unichr). Она принимает целое число и возвращает строку Юникода длиной 1, содержащую соответствующую кодовую точку. Обратная операция – встроенная функция [`ord()`](https://python-all.ru/2.7/library/functions.html#ord), которая принимает односимвольную строку Юникода и возвращает значение кодовой точки:156157```python158>>> unichr(40960)159u'\ua000'160>>> ord(u'\ua000')16140960162```163164Экземпляры типа [`unicode`](https://python-all.ru/2.7/library/functions.html#unicode) имеют много тех же методов, что и 8-битный строковый тип, для таких операций, как поиск и форматирование:165166```python167>>> s = u'Was ever feather so lightly blown to and fro as this multitude?'168>>> s.count('e')1695170>>> s.find('feather')1719172>>> s.find('bird')173-1174>>> s.replace('feather', 'sand')175u'Was ever sand so lightly blown to and fro as this multitude?'176>>> s.upper()177u'WAS EVER FEATHER SO LIGHTLY BLOWN TO AND FRO AS THIS MULTITUDE?'178```179180Обратите внимание: аргументы этих методов могут быть строками Unicode или 8-битными строками. Перед выполнением операции 8-битные строки будут преобразованы в Unicode; при этом будет использоваться кодировка ASCII по умолчанию, поэтому символы больше 127 приведут к исключению:181182```python183>>> s.find('Was\x9f')                   184Traceback (most recent call last):185    ...186UnicodeDecodeError: 'ascii' codec can't decode byte 0x9f in position 3:187ordinal not in range(128)188>>> s.find(u'Was\x9f')189-1190```191192Большая часть кода Python, работающего со строками, поэтому будет работать и со строками Unicode без каких-либо изменений. (Коду ввода-вывода требуется более серьёзная адаптация для Unicode; подробнее об этом позже.)193194Ещё один важный метод – `.encode([encoding], [errors='strict'])`, который возвращает 8-битную строковую версию строки Unicode, закодированную в запрошенной кодировке. Параметр `errors` аналогичен параметру конструктора `unicode()`, но с одной дополнительной возможностью: помимо ‘strict’, ‘ignore’ и ‘replace’ можно также передать ‘xmlcharrefreplace’, использующий символьные ссылки XML. Следующий пример показывает различные результаты:195196```python197>>> u = unichr(40960) + u'abcd' + unichr(1972)198>>> u.encode('utf-8')199'\xea\x80\x80abcd\xde\xb4'200>>> u.encode('ascii')                       201Traceback (most recent call last):202    ...203UnicodeEncodeError: 'ascii' codec can't encode character u'\ua000' in204position 0: ordinal not in range(128)205>>> u.encode('ascii', 'ignore')206'abcd'207>>> u.encode('ascii', 'replace')208'?abcd?'209>>> u.encode('ascii', 'xmlcharrefreplace')210'&#40960;abcd&#1972;'211```2122138-битные строки Python имеют метод `.decode([encoding], [errors])`, который интерпретирует строку с использованием заданной кодировки:214215```python216>>> u = unichr(40960) + u'abcd' + unichr(1972)   # Собрать строку217>>> utf8_version = u.encode('utf-8')             # Закодировать в UTF-8218>>> type(utf8_version), utf8_version219(<type 'str'>, '\xea\x80\x80abcd\xde\xb4')220>>> u2 = utf8_version.decode('utf-8')            # Декодировать с использованием UTF-8221>>> u == u2                                      # Две строки совпадают222True223```224225Низкоуровневые процедуры регистрации и доступа к доступным кодировкам находятся в модуле [`codecs`](https://python-all.ru/2.7/library/codecs.html#module-codecs). Однако функции кодирования и декодирования, возвращаемые этим модулем, обычно слишком низкоуровневые для комфортного использования, поэтому я не буду описывать модуль [`codecs`](https://python-all.ru/2.7/library/codecs.html#module-codecs) здесь. Если вам нужно реализовать совершенно новую кодировку, придётся изучить интерфейсы модуля [`codecs`](https://python-all.ru/2.7/library/codecs.html#module-codecs), но реализация кодировок – узкоспециализированная задача, которая также не будет рассматриваться здесь. Для получения более подробной информации обратитесь к документации Python.226227Наиболее часто используемой частью модуля [`codecs`](https://python-all.ru/2.7/library/codecs.html#module-codecs) является функция [`codecs.open()`](https://python-all.ru/2.7/library/codecs.html#codecs.open), которая будет рассмотрена в разделе, посвящённом вводу и выводу.228229### Литералы Unicode в исходном коде Python230231В исходном коде Python литералы Unicode записываются как строки с префиксом ‘u’ или ‘U’: `u'abcdefghijk'`. Конкретные кодовые точки можно записать с помощью управляющей последовательности `\u`, после которой следуют четыре шестнадцатеричные цифры, задающие кодовую точку. Управляющая последовательность `\U` аналогична, но требует 8 шестнадцатеричных цифр, а не 4.232233Литералы Unicode также могут использовать те же управляющие последовательности, что и 8-битные строки, включая `\x`, но `\x` принимает только две шестнадцатеричные цифры, поэтому не может выразить произвольную кодовую точку. Восьмеричные escape-последовательности могут доходить до U+01ff, что в восьмеричной системе равно 777.234235```python236>>> s = u"a\xac\u1234\u20ac\U00008000"237... #      ^^^^ двузначная шестнадцатеричная escape-последовательность238... #          ^^^^^^ четырёхзначная Unicode escape-последовательность239... #                      ^^^^^^^^^^ восьмизначная Unicode escape-последовательность240>>> for c in s:  print ord(c),241...24297 172 4660 8364 32768243```244245Использование управляющих последовательностей для кодовых точек больше 127 вполне допустимо в небольших количествах, но становится утомительным, если используется много символов с диакритическими знаками, как, например, в программе с сообщениями на французском или другом языке, использующем диакритику. Также можно собирать строки с помощью встроенной функции [`unichr()`](https://python-all.ru/2.7/library/functions.html#unichr), но это ещё более трудоёмко.246247В идеале хотелось бы иметь возможность записывать литералы в естественной кодировке вашего языка. Тогда можно было бы редактировать исходный код Python в любимом редакторе, который будет отображать символы с диакритическими знаками естественно, а при выполнении программы будут использоваться правильные символы.248249Python поддерживает запись литералов Unicode в любой кодировке, но необходимо объявить используемую кодировку. Это делается путём включения специального комментария либо в первую, либо во вторую строку исходного файла:250251```python252#!/usr/bin/env python253# -*- coding: latin-1 -*-254255u = u'abcdé'256print ord(u[-1])257```258259Синтаксис вдохновлён нотацией Emacs для указания локальных переменных файла. Emacs поддерживает множество различных переменных, но Python поддерживает только ‘coding’. Символы `-*-` указывают Emacs, что комментарий специальный; они не имеют значения для Python, но являются соглашением. Python ищет `coding: name` или `coding=name` в комментарии.260261Если не включить такой комментарий, по умолчанию будет использоваться кодировка ASCII. Версии Python до 2.4 были евроцентричными и по умолчанию использовали Latin-1 для строковых литералов; в Python 2.4 символы больше 127 по-прежнему работают, но выдают предупреждение. Например, следующая программа не имеет объявления кодировки:262263```python264#!/usr/bin/env python265u = u'abcdé'266print ord(u[-1])267```268269При запуске на Python 2.4 будет выведено следующее предупреждение:270271```python272amk:~$ python2.4 p263.py273sys:1: DeprecationWarning: Non-ASCII character '\xe9'274     in file p263.py on line 2, but no encoding declared;275     see https://www.python.org/peps/pep-0263.html for details276```277278Python 2.5 и выше более строги и выдадут синтаксическую ошибку:279280```python281amk:~$ python2.5 p263.py282File "/tmp/p263.py", line 2283SyntaxError: Non-ASCII character '\xc3' in file /tmp/p263.py284  on line 2, but no encoding declared; see285  https://www.python.org/peps/pep-0263.html for details286```287288### Свойства Unicode289290Спецификация Unicode включает базу данных информации о кодовых точках. Для каждой определённой кодовой точки информация включает имя символа, его категорию, числовое значение, если применимо (Unicode имеет символы, представляющие римские цифры и дроби, такие как одна треть и четыре пятых). Также есть свойства, связанные с использованием кодовой точки в двунаправленном тексте и другие свойства, связанные с отображением.291292Следующая программа выводит некоторую информацию о нескольких символах и печатает числовое значение одного конкретного символа:293294```python295import unicodedata296297u = unichr(233) + unichr(0x0bf2) + unichr(3972) + unichr(6000) + unichr(13231)298299for i, c in enumerate(u):300    print i, '%04x' % ord(c), unicodedata.category(c),301    print unicodedata.name(c)302303# Получить числовое значение второго символа304print unicodedata.numeric(u[1])305```306307При запуске выводится:308309```python3100 00e9 Ll LATIN SMALL LETTER E WITH ACUTE3111 0bf2 No TAMIL NUMBER ONE THOUSAND3122 0f84 Mn TIBETAN MARK HALANTA3133 1770 Lo TAGBANWA LETTER SA3144 33af So SQUARE RAD OVER S SQUARED3151000.0316```317318Коды категорий – это аббревиатуры, описывающие природу символа. Они сгруппированы в категории, такие как «Буква», «Число», «Пунктуация» или «Символ», которые, в свою очередь, делятся на подкатегории. Взяв коды из приведённого выше вывода, `'Ll'` означает «Буква, строчная», `'No'` – «Число, прочее», `'Mn'` – «Знак, некомбинируемый», а `'So'` – «Символ, прочее». Список кодов категорий см. на странице \<[http://www.unicode.org/reports/tr44/#General\_Category\_Values](https://python-all.ru/2.7/howto/unicode.html)\>.319320### Ссылки321322Типы Unicode и 8-битных строк описаны в справочнике по библиотеке Python в разделе [Типы последовательностей – str, unicode, list, tuple, bytearray, buffer, xrange](https://python-all.ru/2.7/library/stdtypes.html#typesseq).323324Документация модуля [`unicodedata`](https://python-all.ru/2.7/library/unicodedata.html#module-unicodedata).325326Документация модуля [`codecs`](https://python-all.ru/2.7/library/codecs.html#module-codecs).327328Марк-Андре Лембург выступил с докладом на EuroPython 2002 под названием «Python и Unicode». PDF-версия его слайдов доступна по адресу \<[https://downloads.egenix.com/python/Unicode-EPC2002-Talk.pdf](https://python-all.ru/2.7/howto/unicode.html)\> и представляет собой отличный обзор архитектуры поддержки Unicode в Python.329330## Чтение и запись данных Unicode331332После написания кода, работающего с данными Unicode, следующая проблема – ввод/вывод. Как передавать строки Unicode в программу и как преобразовывать Unicode в форму, подходящую для хранения или передачи?333334Возможно, вам может не понадобиться ничего делать в зависимости от источников ввода и мест назначения вывода; следует проверить, поддерживают ли библиотеки, используемые в вашем приложении, Unicode нативно. Например, XML-парсеры часто возвращают данные в Unicode. Многие реляционные базы данных также поддерживают столбцы со значением Unicode и могут возвращать значения Unicode из SQL-запросов.335336Данные Unicode обычно преобразуются в определённую кодировку перед записью на диск или отправкой через сокет. Всю эту работу можно сделать вручную: открыть файл, прочитать 8-битную строку из него и преобразовать строку с помощью `unicode(str, encoding)`. Однако ручной подход не рекомендуется.337338Одна из проблем – многобайтовая природа кодировок: один символ Unicode может быть представлен несколькими байтами. Если нужно читать файл фрагментами произвольного размера (скажем, 1 КБ или 4 КБ), придётся писать код обработки ошибок для случаев, когда на границе фрагмента оказалась только часть байтов, кодирующих один символ Unicode. Одно из решений – прочитать весь файл в память и затем декодировать, но это не позволяет работать с очень большими файлами: если нужно прочитать файл размером 2 ГБ, потребуется 2 ГБ оперативной памяти. (Даже больше, ведь в какой-то момент в памяти должны находиться и закодированная строка, и её версия в Unicode.)339340Решением было бы использование низкоуровневого интерфейса декодирования для обработки случая частичных кодовых последовательностей. Работа по реализации этого уже проделана за вас: модуль [`codecs`](https://python-all.ru/2.7/library/codecs.html#module-codecs) содержит версию функции [`open()`](https://python-all.ru/2.7/library/functions.html#open), которая возвращает файлоподобный объект, предполагающий, что содержимое файла находится в указанной кодировке, и принимающий параметры Unicode для таких методов, как `.read()` и `.write()`.341342Параметры функции: `open(filename, mode='rb', encoding=None, errors='strict', buffering=1)`. `mode` может быть `'r'`, `'w'` или `'a'`, как и соответствующий параметр стандартной встроенной функции `open()`; добавьте `'+'` для обновления файла. `buffering` аналогичен параметру стандартной функции. `encoding` – это строка, задающая используемую кодировку; если оставить её как `None`, возвращается обычный файловый объект Python, принимающий 8-битные строки. В противном случае возвращается объект-обёртка, и данные, записываемые в обёртку или читаемые из неё, будут преобразованы по мере необходимости. `errors` задаёт действие при ошибках кодирования и может принимать одно из обычных значений: ‘strict’, ‘ignore’ или ‘replace’.343344Чтение Unicode из файла, таким образом, просто:345346```python347import codecs348f = codecs.open('unicode.rst', encoding='utf-8')349for line in f:350    print repr(line)351```352353Также можно открывать файлы в режиме обновления, позволяющем как чтение, так и запись:354355```python356f = codecs.open('test', encoding='utf-8', mode='w+')357f.write(u'\u4500 blah blah blah\n')358f.seek(0)359print repr(f.readline()[:1])360f.close()361```362363Символ Unicode U+FEFF используется в качестве метки порядка байтов (BOM) и часто записывается как первый символ файла для облегчения автоматического определения порядка байтов. Некоторые кодировки, такие как UTF-16, ожидают наличие BOM в начале файла; при использовании такой кодировки BOM автоматически записывается как первый символ и незаметно удаляется при чтении файла. Существуют варианты этих кодировок, например ‘utf-16-le’ и ‘utf-16-be’ для little-endian и big-endian соответственно, которые задают определённый порядок байтов и не пропускают BOM.364365### Имена файлов в Unicode366367Большинство широко используемых сегодня операционных систем поддерживают имена файлов, содержащие произвольные символы Unicode. Обычно это реализуется преобразованием строки Unicode в некую кодировку, которая зависит от системы. Например, Mac OS X использует UTF-8, а Windows – настраиваемую кодировку; в Windows Python использует имя «mbcs» для обозначения текущей настроенной кодировки. В Unix-системах кодировка файловой системы существует только в том случае, если установлены переменные окружения `LANG` или `LC_CTYPE`; если они не установлены, используется кодировка ASCII.368369Функция [`sys.getfilesystemencoding()`](https://python-all.ru/2.7/library/sys.html#sys.getfilesystemencoding) возвращает кодировку для использования в текущей системе, на случай если потребуется выполнить кодирование вручную, но в этом нет особой необходимости. При открытии файла для чтения или записи обычно можно просто передать строку Unicode в качестве имени файла, и она будет автоматически преобразована в нужную кодировку:370371```python372filename = u'filename\u4500abc'373f = open(filename, 'w')374f.write('blah\n')375f.close()376```377378Функции модуля [`os`](https://python-all.ru/2.7/library/os.html#module-os), такие как [`os.stat()`](https://python-all.ru/2.7/library/os.html#os.stat), также принимают имена файлов Unicode.379380Функция [`os.listdir()`](https://python-all.ru/2.7/library/os.html#os.listdir), возвращающая имена файлов, ставит вопрос: должна ли она возвращать версию имён файлов в Unicode или 8-битные строки с закодированными версиями? [`os.listdir()`](https://python-all.ru/2.7/library/os.html#os.listdir) делает и то, и другое в зависимости от того, передан ли путь к каталогу как 8-битная строка или строка Unicode. Если передать путь как строку Unicode, имена файлов будут декодированы с использованием кодировки файловой системы и будет возвращён список строк Unicode, а при передаче 8-битного пути будут возвращены 8-битные версии имён файлов. Например, если предположить, что кодировка файловой системы по умолчанию – UTF-8, выполнение следующей программы:381382```python383fn = u'filename\u4500abc'384f = open(fn, 'w')385f.close()386387import os388print os.listdir('.')389print os.listdir(u'.')390```391392выдаст следующий результат:393394```console395amk:~$ python t.py396['.svn', 'filename\xe4\x94\x80abc', ...]397[u'.svn', u'filename\u4500abc', ...]398```399400Первый список содержит имена файлов в кодировке UTF-8, а второй список – версии в Unicode.401402### Советы по написанию программ с поддержкой Unicode403404В этом разделе приводятся некоторые рекомендации по написанию программного обеспечения, работающего с Unicode.405406Самый важный совет:407408> Программное обеспечение должно работать только со строками Unicode внутри себя, выполняя преобразование в определённую кодировку на выходе.409410Если попытаться написать функции обработки, принимающие как строки Unicode, так и 8-битные строки, программа будет уязвима для ошибок в местах объединения строк разных типов. Кодировка Python по умолчанию – ASCII, поэтому при появлении символа со значением ASCII \> 127 во входных данных, возникнет [`UnicodeDecodeError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.UnicodeDecodeError), так как ASCII не может обработать такой символ.411412Такие проблемы легко упустить, если тестировать программу только на данных без диакритических знаков: всё будет казаться рабочим, но на самом деле в программе есть ошибка, которая ждёт первого же пользователя, попытавшегося использовать символы \> 127. Поэтому второй совет таков:413414> Включайте в тестовые данные символы \> 127, а ещё лучше – символы \> 255.415416При работе с данными из веб-браузера или другого ненадёжного источника распространённый приём – проверять строку на недопустимые символы перед её использованием в генерируемой командной строке или сохранением в базу данных. Если это делается, следует проверять строку в том виде, в котором она будет использована или сохранена; кодировки могут маскировать символы. Это особенно важно, если входные данные также задают кодировку; многие кодировки не затрагивают часто проверяемые символы, но Python включает такие кодировки, как `'base64'`, которые изменяют каждый символ.417418Например, предположим, есть система управления контентом, которая принимает имя файла в Unicode, и нужно запретить пути с символом '/'. Можно написать такой код:419420```python421def read_file (filename, encoding):422    if '/' in filename:423        raise ValueError("'/' not allowed in filenames")424    unicode_name = filename.decode(encoding)425    f = open(unicode_name, 'r')426    # ... вернуть содержимое файла ...427```428429Однако, если злоумышленник сможет указать кодировку `'base64'`, он сможет передать `'L2V0Yy9wYXNzd2Q='` – это base-64-кодированная форма строки `'/etc/passwd'`, чтобы прочитать системный файл. Приведённый выше код ищет символы `'/'` в закодированной форме и пропускает опасный символ в результирующей декодированной форме.430431### Ссылки432433PDF-слайды презентации Марка-Андре Лембурга «Writing Unicode-aware Applications in Python» доступны по адресу \<[https://downloads.egenix.com/python/LSM2005-Developing-Unicode-aware-applications-in-Python.pdf](https://python-all.ru/2.7/howto/unicode.html)\> и освещают вопросы кодировок символов, а также интернационализацию и локализацию приложений.434435## История изменений и благодарности436437Благодарности следующим людям, которые указали на ошибки или предложили улучшения для этой статьи: Nicholas Bastin, Marius Gedminas, Kent Johnson, Ken Krugler, Marc-André Lemburg, Martin von Löwis, Chad Whitacre.438439Версия 1.0: опубликована 5 августа 2005 года.440441Версия 1.01: опубликована 7 августа 2005 года. Исправлены фактические ошибки и ошибки разметки; добавлено несколько ссылок.442443Версия 1.02: опубликована 16 августа 2005 года. Исправлены фактические ошибки.444445Версия 1.03: опубликована 20 июня 2010 года. Отмечается, что Python 3.x не рассматривается, и данное руководство охватывает только версию 2.x.446