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

unicode.md

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

1> **Источник:** https://python-all.ru/3.6/howto/unicode.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Руководство по Unicode89**Версия**10111.121213В этом HOWTO рассматривается поддержка Unicode в Python и объясняются различные проблемы, с которыми люди часто сталкиваются при работе с Unicode.1415## Введение в Unicode1617### История кодировок символов1819В 1968 году был стандартизирован American Standard Code for Information Interchange, более известный под аббревиатурой ASCII. ASCII определил числовые коды для различных символов, причем числовые значения варьировались от 0 до 127. Например, строчная буква 'a' получила код 97.2021ASCII был стандартом, разработанным в США, поэтому он определял только символы без диакритических знаков. Буква 'e' была, а 'é' или 'Í' – нет. Это означало, что языки, требующие диакритических знаков, не могли быть точно представлены в ASCII. (На самом деле отсутствие диакритических знаков имеет значение и для английского, в котором есть такие слова, как 'naïve' и 'café', а некоторые издательства требуют написания вроде 'coöperate'.)2223Некоторое время люди просто писали программы, которые не отображали диакритические знаки. В середине 1980-х программа на Apple II BASIC, написанная носителем французского языка, могла содержать строки вроде этих:2425```basic26PRINT "MISE A JOUR TERMINEE"27PRINT "PARAMETRES ENREGISTRES"28```2930Эти сообщения должны содержать диакритические знаки (terminée, paramètre, enregistrés) и они выглядят неправильно для того, кто понимает французский.3132В 1980-х почти все персональные компьютеры были 8-битными, то есть байты могли хранить значения от 0 до 255. Коды ASCII доходили только до 127, поэтому некоторые машины назначали значения от 128 до 255 символам с диакритическими знаками. Однако разные машины имели разные коды, что приводило к проблемам при обмене файлами. Со временем появились различные широко используемые наборы значений для диапазона 128–255. Некоторые были настоящими стандартами, определёнными Международной организацией по стандартизации, а некоторые были *де-факто* соглашениями, придуманными той или иной компанией и получившими распространение.3334255 символов – это не так много. Например, невозможно вместить и диакритические символы, используемые в Западной Европе, и кириллицу, используемую для русского языка, в диапазон 128–255, потому что таких символов больше 128.3536Можно было записывать файлы, используя разные кодировки (все русские файлы в кодировке KOI8, все французские файлы в другой кодировке Latin1), но что, если нужно было написать французский документ, цитирующий русский текст? В 1980-х люди начали искать решение этой проблемы, и началась работа по стандартизации Unicode.3738Unicode изначально использовал 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 сильно упрощено. Точные исторические детали не обязательны для понимания того, как эффективно использовать Unicode, но если вам интересно, обратитесь к сайту консорциума Unicode, указанному в разделе «Ссылки», или к [статье в Википедии об Unicode](https://python-all.ru/3.6/howto/unicode.html) для получения дополнительной информации.)4344### Определения4546**Символ** – это наименьший возможный компонент текста. 'A', 'B', 'C' и т.д. – всё это разные символы. Также как 'È' и 'Í'. Символы – это абстракции, и они варьируются в зависимости от языка или контекста. Например, символ ома (Ω) обычно рисуется очень похоже на заглавную букву омега (Ω) в греческом алфавите (в некоторых шрифтах они могут даже совпадать), но это два разных символа с разными значениями.4748Стандарт Unicode описывает, как символы представляются с помощью **кодовых точек**. Кодовая точка – это целое число, обычно обозначаемое в шестнадцатеричной системе. В стандарте кодовая точка записывается с использованием обозначения `U+12CA` для обозначения символа со значением `0x12ca` (4 810 в десятичной системе). Стандарт Unicode содержит множество таблиц, перечисляющих символы и соответствующие им кодовые точки:4950```text510061    '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` (1 114 111 в десятичной системе). Эта последовательность должна быть представлена в памяти в виде набора байтов (то есть значений от 0 до 255). Правила преобразования строки Unicode в последовательность байтов называются **кодировкой**.6566Первая кодировка, которая может прийти в голову – это массив 32-битных целых чисел. В этом представлении строка «Python» будет выглядеть так:6768```text69   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, поэтому значительная часть места занята `0x00` байтами. Приведённая выше строка занимает 24 байта, тогда как в ASCII-представлении достаточно 6 байт. Увеличение расхода оперативной памяти не так критично (настольные компьютеры имеют гигабайты RAM, а строки редко бывают очень большими), однако четырёхкратное расширение при использовании дискового пространства и сетевой пропускной способности уже неприемлемо.783. Оно несовместимо с существующими C-функциями, такими как `strlen()`, поэтому пришлось бы использовать новое семейство функций для работы с широкими строками.794. Многие интернет-стандарты определены в терминах текстовых данных и не могут обрабатывать содержимое с встроенными нулевыми байтами.8081Обычно люди не используют эту кодировку, предпочитая другие кодировки, которые более эффективны и удобны. UTF-8, вероятно, самая распространённая поддерживаемая кодировка; она будет рассмотрена ниже.8283Кодировки не обязаны обрабатывать все возможные символы Unicode, и большинство кодировок этого не делают. Правила преобразования строки Unicode в кодировку ASCII, например, просты; для каждой кодовой точки:84851. Если кодовая точка \< 128, каждый байт равен значению кодовой точки.862. Если кодовая точка равна 128 или больше, строка Unicode не может быть представлена в этой кодировке. (В этом случае Python возбуждает исключение [`UnicodeEncodeError`](https://python-all.ru/3.6/library/exceptions.html#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-32, но они используются реже, чем UTF-8.) UTF-8 использует следующие правила:93941. Если кодовая точка \< 128, она представляется соответствующим значением байта.952. Если кодовая точка \>= 128, она преобразуется в последовательность из двух, трёх или четырёх байт, причём каждый байт находится в диапазоне от 128 до 255.9697UTF-8 обладает несколькими удобными свойствами:98991. Она может обрабатывать любую кодовую точку Юникода.1002. Строка Unicode преобразуется в последовательность байтов, не содержащую встроенных нулевых байтов. Это позволяет избежать проблем с порядком байтов и означает, что строки UTF-8 могут обрабатываться функциями C, такими как `strcpy()`, и передаваться по протоколам, которые не могут обрабатывать нулевые байты.1013. Любая строка ASCII также является корректным текстом UTF-8.1024. UTF-8 довольно компактна; большинство часто используемых символов можно представить одним или двумя байтами.1035. Если байты повреждены или потеряны, можно определить начало следующей UTF-8-кодовой точки и восстановить синхронизацию. Кроме того, маловероятно, что случайные 8-битные данные будут выглядеть как корректный UTF-8.104105### Ссылки106107The [Unicode Consortium site](https://python-all.ru/3.6/howto/unicode.html) has character charts, a glossary, and PDF versions of the Unicode specification. Be prepared for some difficult reading. [A chronology](https://python-all.ru/3.6/howto/unicode.html) of the origin and development of Unicode is also available on the site.108109To help understand the standard, Jukka Korpela has written [an introductory guide](https://python-all.ru/3.6/howto/unicode.html) to reading the Unicode character tables.110111Another [good introductory article](https://python-all.ru/3.6/howto/unicode.html) was written by Joel Spolsky. If this introduction didn’t make things clear to you, you should try reading this alternate article before continuing.112113Wikipedia entries are often helpful; see the entries for “[character encoding](https://python-all.ru/3.6/howto/unicode.html)” and [UTF-8](https://python-all.ru/3.6/howto/unicode.html), for example.114115## Поддержка Юникода в Python116117Теперь, когда вы освоили основы Юникода, давайте рассмотрим возможности Python по работе с ним.118119### Тип строки120121Начиная с Python 3.0, в языке появился тип [`str`](https://python-all.ru/3.6/library/stdtypes.html#str), содержащий символы Unicode, то есть любая строка, созданная с помощью `"unicode rocks!"`, `'unicode rocks!'` или синтаксиса тройных кавычек, хранится как Unicode.122123Кодировка исходного кода Python по умолчанию – UTF-8, поэтому можно просто включить символ Юникода в строковый литерал:124125```python126try:127    with open('/tmp/input.txt', 'r') as f:128        ...129except OSError:130    # Сообщение об ошибке 'File not found'.131    print("Fichier non trouvé")132```133134Можно использовать другую кодировку, отличную от UTF-8, поместив специально оформленный комментарий в первую или вторую строку исходного кода:135136```python137# -*- coding: <encoding name> -*-138```139140Попутное замечание: Python 3 также поддерживает использование символов Юникода в идентификаторах:141142```python143répertoire = "/tmp/records.log"144with open(répertoire, "w") as f:145    f.write("test\n")146```147148Если вы не можете ввести нужный символ в редакторе или по какой-то причине хотите сохранить исходный код только в ASCII, можно использовать escape-последовательности в строковых литералах. (В зависимости от системы вместо u-escape может отображаться сама заглавная дельта.)149150```python151>>> "\N{GREEK CAPITAL LETTER DELTA}"  # Использование имени символа152'\u0394'153>>> "\u0394"                          # Использование 16-битного шестнадцатеричного значения154'\u0394'155>>> "\U00000394"                      # Использование 32-битного шестнадцатеричного значения156'\u0394'157```158159Кроме того, строку можно создать методом [`decode()`](https://python-all.ru/3.6/library/stdtypes.html#bytes.decode) объекта [`bytes`](https://python-all.ru/3.6/library/stdtypes.html#bytes). Этот метод принимает аргумент *encoding* (кодировка), например `UTF-8`, и опционально аргумент *errors* (реакция на ошибки).160161Аргумент *errors* определяет реакцию, когда входную строку невозможно преобразовать согласно правилам кодировки. Допустимые значения этого аргумента: `'strict'` (вызывает исключение [`UnicodeDecodeError`](https://python-all.ru/3.6/library/exceptions.html#UnicodeDecodeError)), `'replace'` (использует `U+FFFD`, `REPLACEMENT CHARACTER`), `'ignore'` (просто исключает символ из результата Юникода) или `'backslashreplace'` (вставляет escape-последовательность `\xNN`). Следующие примеры показывают различия:162163```python164>>> b'\x80abc'.decode("utf-8", "strict")  165Traceback (most recent call last):166    ...167UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0:168  invalid start byte169>>> b'\x80abc'.decode("utf-8", "replace")170'\ufffdabc'171>>> b'\x80abc'.decode("utf-8", "backslashreplace")172'\\x80abc'173>>> b'\x80abc'.decode("utf-8", "ignore")174'abc'175```176177Кодировки задаются строками, содержащими название кодировки. Python 3.2 поставляется примерно со 100 различными кодировками; список можно найти в справочнике по библиотеке Python в разделе [Стандартные кодировки](https://python-all.ru/3.6/library/codecs.html#standard-encodings). Некоторые кодировки имеют несколько названий; например, `'latin-1'`, `'iso_8859_1'` и `'8859`’ – всё это синонимы одной и той же кодировки.178179Односимвольные строки Юникода можно также создать с помощью встроенной функции [`chr()`](https://python-all.ru/3.6/library/functions.html#chr). Она принимает целое число и возвращает строку Юникода длиной 1, содержащую соответствующую кодовую точку. Обратная операция – встроенная функция [`ord()`](https://python-all.ru/3.6/library/functions.html#ord), которая принимает односимвольную строку Юникода и возвращает значение кодовой точки:180181```python182>>> chr(57344)183'\ue000'184>>> ord('\ue000')18557344186```187188### Преобразование в байты189190Противоположным методом для [`bytes.decode()`](https://python-all.ru/3.6/library/stdtypes.html#bytes.decode) является [`str.encode()`](https://python-all.ru/3.6/library/stdtypes.html#str.encode), который возвращает [`bytes`](https://python-all.ru/3.6/library/stdtypes.html#bytes) представление строки Unicode, закодированное в требуемой *кодировке*.191192Параметр *errors* такой же, как и параметр метода [`decode()`](https://python-all.ru/3.6/library/stdtypes.html#bytes.decode), но поддерживает несколько дополнительных обработчиков. Помимо `'strict'`, `'ignore'` и `'replace'` (который в данном случае вставляет знак вопроса вместо некодируемого символа), есть также `'xmlcharrefreplace'` (вставляет ссылку на символ XML), `backslashreplace` (вставляет управляющую последовательность `\uNNNN`) и `namereplace` (вставляет управляющую последовательность `\N{...}`).193194В следующем примере показаны различные результаты:195196```python197>>> u = chr(40960) + 'abcd' + chr(1972)198>>> u.encode('utf-8')199b'\xea\x80\x80abcd\xde\xb4'200>>> u.encode('ascii')  201Traceback (most recent call last):202    ...203UnicodeEncodeError: 'ascii' codec can't encode character '\ua000' in204  position 0: ordinal not in range(128)205>>> u.encode('ascii', 'ignore')206b'abcd'207>>> u.encode('ascii', 'replace')208b'?abcd?'209>>> u.encode('ascii', 'xmlcharrefreplace')210b'&#40960;abcd&#1972;'211>>> u.encode('ascii', 'backslashreplace')212b'\\ua000abcd\\u07b4'213>>> u.encode('ascii', 'namereplace')214b'\\N{YI SYLLABLE IT}abcd\\u07b4'215```216217Низкоуровневые функции для регистрации и доступа к доступным кодировкам находятся в модуле [`codecs`](https://python-all.ru/3.6/library/codecs.html#module-codecs). Реализация новых кодировок также требует понимания модуля [`codecs`](https://python-all.ru/3.6/library/codecs.html#module-codecs). Однако функции кодирования и декодирования, возвращаемые этим модулем, обычно слишком низкоуровневые для комфортной работы, а написание новых кодировок является специализированной задачей, поэтому этот модуль не будет рассматриваться в данном HOWTO.218219### Литералы Unicode в исходном коде Python220221В исходном коде Python конкретные кодовые точки Unicode можно записать с помощью управляющей последовательности `\u`, за которой следуют четыре шестнадцатеричные цифры, задающие кодовую точку. Управляющая последовательность `\U` аналогична, но ожидает восемь шестнадцатеричных цифр, а не четыре:222223```python224>>> s = "a\xac\u1234\u20ac\U00008000"225... #     ^^^^ двузначная шестнадцатеричная escape-последовательность226... #         ^^^^^^ четырёхзначная Unicode escape-последовательность227... #                     ^^^^^^^^^^ восьмизначная Unicode escape-последовательность228>>> [ord(c) for c in s]229[97, 172, 4660, 8364, 32768]230```231232Использование управляющих последовательностей для кодовых точек больше 127 вполне допустимо в небольших количествах, но становится утомительным, если используется много символов с диакритическими знаками, как, например, в программе с сообщениями на французском или другом языке, использующем диакритику. Также можно собирать строки с помощью встроенной функции [`chr()`](https://python-all.ru/3.6/library/functions.html#chr), но это ещё более трудоёмко.233234В идеале хотелось бы иметь возможность записывать литералы в естественной кодировке вашего языка. Тогда можно было бы редактировать исходный код Python в любимом редакторе, который будет отображать символы с диакритическими знаками естественно, а при выполнении программы будут использоваться правильные символы.235236По умолчанию Python поддерживает запись исходного кода в UTF-8, но можно использовать почти любую кодировку, если объявить используемую кодировку. Это делается путём включения специального комментария в первую или вторую строку исходного файла:237238```python239#!/usr/bin/env python240# -*- coding: latin-1 -*-241242u = 'abcdé'243print(ord(u[-1]))244```245246Синтаксис вдохновлён нотацией Emacs для указания локальных переменных файла. Emacs поддерживает множество различных переменных, но Python поддерживает только ‘coding’. Символы `-*-` указывают Emacs, что комментарий специальный; они не имеют значения для Python, но являются соглашением. Python ищет `coding: name` или `coding=name` в комментарии.247248Если такой комментарий не включён, по умолчанию будет использоваться кодировка UTF-8, как уже упоминалось. См. также [**PEP 263**](https://python-all.ru/3.6/howto/unicode.html) для получения дополнительной информации.249250### Свойства Unicode251252Спецификация Unicode включает базу данных информации о кодовых точках. Для каждой определённой кодовой точки информация включает имя символа, его категорию, числовое значение, если применимо (Unicode содержит символы, представляющие римские цифры и дроби, такие как одна треть и четыре пятых). Существуют также свойства, связанные с использованием кодовой точки в двунаправленном тексте и другие свойства, относящиеся к отображению.253254Следующая программа выводит некоторую информацию о нескольких символах и печатает числовое значение одного конкретного символа:255256```python257import unicodedata258259u = chr(233) + chr(0x0bf2) + chr(3972) + chr(6000) + chr(13231)260261for i, c in enumerate(u):262    print(i, '%04x' % ord(c), unicodedata.category(c), end=" ")263    print(unicodedata.name(c))264265# Получить числовое значение второго символа266print(unicodedata.numeric(u[1]))267```268269При запуске выводится:270271```text2720 00e9 Ll LATIN SMALL LETTER E WITH ACUTE2731 0bf2 No TAMIL NUMBER ONE THOUSAND2742 0f84 Mn TIBETAN MARK HALANTA2753 1770 Lo TAGBANWA LETTER SA2764 33af So SQUARE RAD OVER S SQUARED2771000.0278```279280Коды категорий – это сокращения, описывающие природу символа. Они группируются в такие категории, как «Буква», «Число», «Знак препинания» или «Символ», которые в свою очередь делятся на подкатегории. Взяв коды из приведённого выше вывода, `'Ll'` означает «Буква, строчная», `'No'` означает «Число, прочее», `'Mn'` – «Знак, некомбинируемый», а `'So'` – «Символ, прочее». См. [раздел «Значения общих категорий» документации по базе данных символов Unicode](https://python-all.ru/3.6/howto/unicode.html) для списка кодов категорий.281282### Регулярные выражения Unicode283284Регулярные выражения, поддерживаемые модулем [`re`](https://python-all.ru/3.6/library/re.html#module-re), могут быть представлены как в виде байтов, так и строк. Некоторые специальные последовательности символов, такие как `\d` и `\w`, имеют разные значения в зависимости от того, задан ли шаблон как байты или строка. Например, `\d` будет соответствовать символам `[0-9]` в байтах, но в строках будет соответствовать любому символу из категории `'Nd'`.285286Строка в этом примере содержит число 57, записанное как тайскими, так и арабскими цифрами:287288```python289import re290p = re.compile(r'\d+')291292s = "Over \u0e55\u0e57 57 flavours"293m = p.search(s)294print(repr(m.group()))295```296297При выполнении `\d+` будет соответствовать тайским цифрам и выведет их на печать. Если передать флаг [`re.ASCII`](https://python-all.ru/3.6/library/re.html#re.ASCII) функции [`compile()`](https://python-all.ru/3.6/library/re.html#re.compile), то `\d+` будет соответствовать подстроке «57».298299Аналогично, `\w` соответствует широкому спектру символов Unicode, но только `[a-zA-Z0-9_]` в байтах или если указан [`re.ASCII`](https://python-all.ru/3.6/library/re.html#re.ASCII), а `\s` будет соответствовать либо пробельным символам Unicode, либо `[ \t\n\r\f\v]`.300301### Ссылки302303Некоторые хорошие альтернативные обсуждения поддержки Unicode в Python:304305- [Обработка текстовых файлов в Python 3](https://python-all.ru/3.6/howto/unicode.html), автор Ник Коглан.306- [Pragmatic Unicode](https://python-all.ru/3.6/howto/unicode.html) – презентация Неда Бэтчелдера на PyCon 2012.307308Тип [`str`](https://python-all.ru/3.6/library/stdtypes.html#str) описан в справочнике по библиотеке Python по адресу [Text Sequence Type – str](https://python-all.ru/3.6/library/stdtypes.html#textseq).309310Документация модуля [`unicodedata`](https://python-all.ru/3.6/library/unicodedata.html#module-unicodedata).311312Документация модуля [`codecs`](https://python-all.ru/3.6/library/codecs.html#module-codecs).313314Марк-Андре Лембург [выступил с презентацией «Python and Unicode» (слайды в PDF)](https://python-all.ru/3.6/howto/unicode.html) на EuroPython 2002. Слайды дают отличный обзор архитектуры поддержки Unicode в Python 2 (где тип строки Unicode называется `unicode`, а литералы начинаются с `u`).315316## Чтение и запись данных Unicode317318После написания кода, работающего с данными Unicode, следующая проблема – ввод/вывод. Как передавать строки Unicode в программу и как преобразовывать Unicode в форму, подходящую для хранения или передачи?319320Возможно, вам может не понадобиться ничего делать в зависимости от источников ввода и мест назначения вывода; следует проверить, поддерживают ли библиотеки, используемые в вашем приложении, Unicode нативно. Например, XML-парсеры часто возвращают данные в Unicode. Многие реляционные базы данных также поддерживают столбцы со значением Unicode и могут возвращать значения Unicode из SQL-запросов.321322Данные Unicode обычно преобразуются в определённую кодировку перед записью на диск или отправкой через сокет. Можно сделать всю работу самостоятельно: открыть файл, прочитать из него 8-битный объект bytes и преобразовать байты с помощью `bytes.decode(encoding)`. Однако ручной подход не рекомендуется.323324Одна из проблем – многобайтовая природа кодировок; один символ Unicode может быть представлен несколькими байтами. Если читать файл произвольными фрагментами (например, по 1024 или 4096 байт), необходимо написать код обработки ошибок для ситуации, когда в конце фрагмента считывается только часть байтов, кодирующих один символ Unicode. Одним из решений было бы прочитать весь файл в память и затем выполнить декодирование, но это не позволяет работать с очень большими файлами; если нужно прочитать файл размером 2 ГБ, потребуется 2 ГБ ОЗУ. (На самом деле больше, поскольку хотя бы на мгновение в памяти должны находиться и закодированная строка, и её Unicode-версия.)325326Решением было бы использование низкоуровневого интерфейса декодирования для перехвата случая частичных последовательностей кодирования. Работа по его реализации уже выполнена за вас: встроенная функция [`open()`](https://python-all.ru/3.6/library/functions.html#open) может вернуть объект, похожий на файл который предполагает, что содержимое файла находится в указанной кодировке, и принимает Unicode параметры для таких методов, как [`read()`](https://python-all.ru/3.6/library/io.html#io.TextIOBase.read) и [`write()`](https://python-all.ru/3.6/library/io.html#io.TextIOBase.write). Это работает через [`open()`](https://python-all.ru/3.6/library/functions.html#open): *encoding* и *errors* параметры, которые интерпретируются так же, как в [`str.encode()`](https://python-all.ru/3.6/library/stdtypes.html#str.encode) и [`bytes.decode()`](https://python-all.ru/3.6/library/stdtypes.html#bytes.decode).327328Чтение Unicode из файла, таким образом, просто:329330```python331with open('unicode.txt', encoding='utf-8') as f:332    for line in f:333        print(repr(line))334```335336Также можно открывать файлы в режиме обновления, позволяющем как чтение, так и запись:337338```python339with open('test', encoding='utf-8', mode='w+') as f:340    f.write('\u4500 blah blah blah\n')341    f.seek(0)342    print(repr(f.readline()[:1]))343```344345Символ Unicode `U+FEFF` используется как метка порядка байтов (BOM) и часто записывается как первый символ файла для облегчения автоматического определения порядка байтов файла. Некоторые кодировки, такие как UTF-16, ожидают наличие BOM в начале файла; при использовании такой кодировки BOM автоматически записывается как первый символ и молча отбрасывается при чтении файла. Существуют варианты этих кодировок, например, ‘utf-16-le’ и ‘utf-16-be’ для little-endian и big-endian кодировок, которые задают определённый порядок байтов и не пропускают BOM.346347В некоторых областях также принято использовать «BOM» в начале файлов, закодированных в UTF-8; это название вводит в заблуждение, поскольку UTF-8 не зависит от порядка байтов. Метка просто объявляет, что файл закодирован в UTF-8. Используйте кодировку 'utf-8-sig', чтобы автоматически пропускать метку, если она присутствует, при чтении таких файлов.348349### Имена файлов в Unicode350351Большинство часто используемых сегодня операционных систем поддерживают имена файлов, содержащие произвольные символы Unicode. Обычно это реализуется путём преобразования строки Unicode в некоторую кодировку, которая зависит от системы. Например, Mac OS X использует UTF-8, а Windows – настраиваемую кодировку; в Windows Python использует имя «mbcs» для обозначения текущей настроенной кодировки. В Unix-системах файловая система получает кодировку только если заданы переменные окружения `LANG` или `LC_CTYPE`; в противном случае используется кодировка UTF-8 по умолчанию.352353Функция [`sys.getfilesystemencoding()`](https://python-all.ru/3.6/library/sys.html#sys.getfilesystemencoding) возвращает кодировку для использования в текущей системе, на случай если потребуется выполнить кодирование вручную, но в этом нет особой необходимости. При открытии файла для чтения или записи обычно можно просто передать строку Unicode в качестве имени файла, и она будет автоматически преобразована в нужную кодировку:354355```python356filename = 'filename\u4500abc'357with open(filename, 'w') as f:358    f.write('blah\n')359```360361Функции модуля [`os`](https://python-all.ru/3.6/library/os.html#module-os), такие как [`os.stat()`](https://python-all.ru/3.6/library/os.html#os.stat), также принимают имена файлов Unicode.362363Функция [`os.listdir()`](https://python-all.ru/3.6/library/os.html#os.listdir) возвращает имена файлов и ставит вопрос: возвращать ли их в виде Unicode или в виде байтов с закодированными версиями? [`os.listdir()`](https://python-all.ru/3.6/library/os.html#os.listdir) делает и то, и другое в зависимости от того, передан ли путь к каталогу как байты или как строка Unicode. Если передать путь в виде строки Unicode, имена файлов декодируются с использованием кодировки файловой системы и возвращается список строк Unicode, а если передать путь как байты, имена файлов возвращаются как байты. Например, если по умолчанию используется кодировка UTF-8, выполнение следующей программы:364365```python366fn = 'filename\u4500abc'367f = open(fn, 'w')368f.close()369370import os371print(os.listdir(b'.'))372print(os.listdir('.'))373```374375выдаст следующий результат:376377```console378amk:~$ python t.py379[b'filename\xe4\x94\x80abc', ...]380['filename\u4500abc', ...]381```382383Первый список содержит имена файлов в кодировке UTF-8, а второй список – версии в Unicode.384385Обратите внимание: в большинстве случаев следует использовать API Unicode. API для байтов стоит использовать только в системах, где могут встретиться не декодируемые имена файлов, то есть в Unix-системах.386387### Советы по написанию программ с поддержкой Unicode388389В этом разделе приводятся некоторые рекомендации по написанию программного обеспечения, работающего с Unicode.390391Самый важный совет:392393> Программное обеспечение должно работать внутри только со строками Unicode, декодируя входные данные как можно раньше и кодируя вывод только в конце.394395При попытке написать функции обработки, которые принимают как строки Unicode, так и байтовые строки, программа окажется уязвима для ошибок везде, где комбинируются два разных типа строк. Автоматического кодирования или декодирования не происходит: если выполнить, например, `str + bytes`, будет возбуждено [`TypeError`](https://python-all.ru/3.6/library/exceptions.html#TypeError).396397При использовании данных из веб-браузера или другого ненадёжного источника распространённый приём – проверка строки на недопустимые символы перед использованием в генерируемой командной строке или сохранением в базе данных. Если это делается, следует проверять декодированную строку, а не закодированные байтовые данные; некоторые кодировки могут обладать интересными свойствами, такими как отсутствие биективности или полной совместимости с ASCII. Это особенно важно, если входные данные также задают кодировку, так как злоумышленник может выбрать хитрый способ скрыть вредоносный текст в закодированном потоке байтов.398399#### Преобразование между кодировками файлов400401Класс [`StreamRecoder`](https://python-all.ru/3.6/library/codecs.html#codecs.StreamRecoder) может прозрачно преобразовывать между кодировками, принимая поток, возвращающий данные в кодировке №1, и ведя себя как поток, возвращающий данные в кодировке №2.402403Например, если есть входной файл *f* в кодировке Latin-1, его можно обернуть с помощью [`StreamRecoder`](https://python-all.ru/3.6/library/codecs.html#codecs.StreamRecoder), чтобы возвращать байты, закодированные в UTF-8:404405```python406new_f = codecs.StreamRecoder(f,407    # кодер/декодер: используется read() для кодирования результатов и408    # с помощью write() для декодирования своего ввода.409    codecs.getencoder('utf-8'), codecs.getdecoder('utf-8'),410411    # читатель/писатель: используется для чтения и записи в поток.412    codecs.getreader('latin-1'), codecs.getwriter('latin-1') )413```414415#### Файлы в неизвестной кодировке416417Что делать, если нужно внести изменения в файл, но кодировка файла неизвестна? Если известно, что кодировка совместима с ASCII и требуется только просмотреть или изменить части в ASCII, можно открыть файл с обработчиком ошибок `surrogateescape`:418419```python420with open(fname, 'r', encoding="ascii", errors="surrogateescape") as f:421    data = f.read()422423# внести изменения в строку 'data'424425with open(fname + '.new', 'w',426          encoding="ascii", errors="surrogateescape") as f:427    f.write(data)428```429430Обработчик ошибок `surrogateescape` декодирует любые не-ASCII байты как кодовые точки в области частного использования Unicode в диапазоне от U+DC80 до U+DCFF. Затем эти частные кодовые точки преобразуются обратно в те же байты, когда при кодировании данных и их записи обратно используется обработчик ошибок `surrogateescape`.431432### Ссылки433434Одна из секций доклада [Mastering Python 3 Input/Output](https://python-all.ru/3.6/howto/unicode.html) на PyCon 2010 (Дэвид Бизли) посвящена обработке текста и работе с двоичными данными.435436В [слайдах PDF презентации Марка-Андре Лембурга «Writing Unicode-aware Applications in Python»](https://python-all.ru/3.6/howto/unicode.html) рассматриваются вопросы кодировок символов, а также интернационализация и локализация приложений. Эти слайды охватывают только Python 2.x.437438[The Guts of Unicode in Python](https://python-all.ru/3.6/howto/unicode.html) – доклад Бенджамина Петерсона на PyCon 2013, в котором обсуждается внутреннее представление Unicode в Python 3.3.439440## Благодарности441442Первоначальный черновик этого документа написал Эндрю Кухлинг. Впоследствии он был доработан Александром Белопольским, Георгом Брандлем, Эндрю Кухлингом и Эцио Мелотти.443444Благодарности следующим людям, которые заметили ошибки или предложили улучшения для этой статьи: Éric Araujo, Nicholas Bastin, Nick Coghlan, Marius Gedminas, Kent Johnson, Ken Krugler, Marc-André Lemburg, Martin von Löwis, Terry J. Reedy, Chad Whitacre.445