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

Руководство по UnicodeUnicode HOWTO

Версия:1.12

В этом HOWTO рассматривается поддержка Unicode в Python и объясняются различные проблемы, с которыми люди часто сталкиваются при работе с Unicode.

Введение в UnicodeIntroduction 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'.)

Некоторое время люди просто писали программы, которые не отображали диакритические знаки. Помню, как смотрел на программы на Apple ][ BASIC, опубликованные во франкоязычных изданиях в середине 1980-х, в которых были такие строки:

PRINT "FICHIER EST COMPLETE."
PRINT "CARACTERE NON ACCEPTE."

Эти сообщения должны содержать диакритические знаки, и они выглядят неправильно для того, кто умеет читать по-французски.

В 1980-х почти все персональные компьютеры были 8-битными, то есть байты могли хранить значения от 0 до 255. Коды ASCII доходили только до 127, поэтому некоторые машины присваивали значения от 128 до 255 символам с диакритическими знаками. Однако разные машины имели разные коды, что приводило к проблемам при обмене файлами. В конце концов появилось несколько часто используемых наборов значений для диапазона 128–255. Одни из них были настоящими стандартами, определёнными Международной организацией по стандартизации, а другие – de facto соглашениями, созданными той или иной компанией и получившими распространение.

255 символов – не так много. Например, невозможно уместить одновременно символы с диакритикой, используемые в Западной Европе, и кириллицу для русского языка в диапазон 128–255, потому что таких символов больше 127.

Можно было записывать файлы, используя разные кодировки (все русские файлы в кодировке KOI8, все французские файлы в другой кодировке Latin1), но что, если нужно было написать французский документ, цитирующий русский текст? В 1980-х люди начали искать решение этой проблемы, и началась работа по стандартизации Unicode.

Изначально Unicode использовал 16-битные символы вместо 8-битных. 16 бит дают 2^16 = 65 536 различных значений, что позволяет представить множество символов из самых разных алфавитов; первоначальная цель заключалась в том, чтобы Unicode включал алфавиты всех языков человечества. Однако оказалось, что даже 16 бит недостаточно для достижения этой цели, и современная спецификация Unicode использует более широкий диапазон кодов: от 0 до 1 114 111 (0x10FFFF в 16-ричной системе).

Существует связанный стандарт ISO – ISO 10646. Unicode и ISO 10646 изначально были разными проектами, но спецификации были объединены в версии Unicode 1.1.

(Это обсуждение истории Unicode сильно упрощено. Не думаю, что среднестатистическому программисту на Python нужно беспокоиться об исторических деталях; обращайтесь к сайту консорциума Unicode, указанному в разделе «Ссылки», за дополнительной информацией.)

ОпределенияDefinitions

Символ – это наименьший возможный компонент текста. 'A', 'B', 'C' и т.д. – всё это разные символы. Также как 'È' и 'Í'. Символы – это абстракции, и они варьируются в зависимости от языка или контекста. Например, символ ома (Ω) обычно рисуется очень похоже на заглавную букву омега (Ω) в греческом алфавите (в некоторых шрифтах они могут даже совпадать), но это два разных символа с разными значениями.

Стандарт Unicode описывает, как символы представляются с помощью кодовых точек. Кодовая точка – это целое число, обычно обозначаемое в 16-ричной системе. В стандарте кодовая точка записывается в нотации 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

Такое представление интуитивно понятно, но его использование порождает ряд проблем.

  1. Оно не является переносимым: разные процессоры по-разному упорядочивают байты.
  2. Это очень расточительно с точки зрения пространства. В большинстве текстов основная часть кодовых точек меньше 127 или меньше 255, поэтому много места занимают байты 0x00. Приведённая выше строка занимает 24 байта по сравнению с 6 байтами, необходимыми для представления в ASCII. Увеличение использования оперативной памяти не так важно (настольные компьютеры имеют гигабайты RAM, а строки обычно не настолько велики), но увеличение использования диска и пропускной способности сети в 4 раза нетерпимо.
  3. Она несовместима с существующими функциями C, такими как strlen(), поэтому пришлось бы использовать новое семейство функций для работы с широкими строками.
  4. Многие интернет-стандарты определены в терминах текстовых данных и не могут обрабатывать содержимое с встроенными нулевыми байтами.

Обычно люди не используют эту кодировку, предпочитая другие кодировки, которые более эффективны и удобны. UTF-8, вероятно, самая распространённая поддерживаемая кодировка; она будет рассмотрена ниже.

Кодировки не обязаны обрабатывать все возможные символы Unicode, и большинство кодировок этого не делают. Правила преобразования строки Unicode в кодировку ASCII, например, просты; для каждой кодовой точки:

  1. Если кодовая точка < 128, каждый байт равен значению кодовой точки.
  2. Если кодовая точка равна 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 использует следующие правила:

  1. Если кодовая точка < 128, она представляется соответствующим значением байта.
  2. Если кодовая точка >= 128, она преобразуется в последовательность из двух, трёх или четырёх байт, причём каждый байт находится в диапазоне от 128 до 255.

UTF-8 обладает несколькими удобными свойствами:

  1. Она может обрабатывать любую кодовую точку Юникода.
  2. Unicode-строка преобразуется в строку байтов, не содержащую встроенных нулевых байтов. Это позволяет избежать проблем с порядком байтов и означает, что строки UTF-8 могут обрабатываться функциями C, такими как strcpy(), и передаваться по протоколам, которые не могут обрабатывать нулевые байты.
  3. Любая строка ASCII также является корректным текстом UTF-8.
  4. UTF-8 довольно компактна; большинство часто используемых символов можно представить одним или двумя байтами.
  5. Если байты повреждены или потеряны, можно определить начало следующей 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.

Ещё одна хорошая вводная статья была написана Джоэлом Спольски. Если это введение не прояснило для вас ситуацию, попробуйте прочитать эту альтернативную статью, прежде чем продолжать.

Wikipedia entries are often helpful; see the entries for “character encoding” and UTF-8, for example.

Поддержка Юникода в PythonPython’s Unicode Support

Теперь, когда вы освоили основы Юникода, давайте рассмотрим возможности Python по работе с ним.

Тип строкиThe String Type

Начиная с Python 3.0, в языке есть тип str, который содержит символы Unicode. Это означает, что любая строка, созданная с помощью "unicode rocks!", 'unicode rocks!' или синтаксиса тройных кавычек, хранится как Unicode.

Чтобы вставить не-ASCII символ Unicode, например, любые буквы с диакритическими знаками, можно использовать управляющие последовательности в строковых литералах следующим образом:

>>> "\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' (просто исключает символ из Unicode-результата). Следующие примеры показывают различия:

>>> 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", "ignore")
'abc'

(В этом примере кода заменяющий символ Unicode заменён вопросительным знаком, поскольку на некоторых системах он может не отображаться.)

Кодировки задаются строками, содержащими имя кодировки. Python 3.2 поставляется примерно со 100 различными кодировками; см. Standard Encodings в Python Library Reference для полного списка. Некоторые кодировки имеют несколько имён; например, 'latin-1', 'iso_8859_1' и '8859‘ – все синонимы одной и той же кодировки.

Односимвольные Unicode-строки также можно создать с помощью встроенной функции chr(), которая принимает целые числа и возвращает Unicode-строку длиной 1, содержащую соответствующую кодовую точку. Обратная операция – встроенная функция ord(), которая принимает односимвольную Unicode-строку и возвращает значение кодовой точки:

>>> chr(57344)
'\ue000'
>>> ord('\ue000')
57344

Преобразование в байтыConverting to Bytes

Противоположный метод для bytes.decode() – это str.encode(), который возвращает bytes-представление строки Unicode, закодированное в указанной кодировке encoding. Параметр errors тот же, что и в методе decode(), с одним дополнительным значением: помимо 'strict', 'ignore' и 'replace' (который в данном случае вставляет вопросительный знак вместо не кодируемого символа), можно также передать 'xmlcharrefreplace', использующий символьные ссылки XML. Следующий пример показывает различные результаты:

>>> 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'&#40960;abcd&#1972;'

Низкоуровневые процедуры для регистрации и доступа к доступным кодировкам находятся в модуле codecs. Однако функции кодирования и декодирования, возвращаемые этим модулем, обычно слишком низкоуровневые для удобного использования, поэтому я не буду описывать модуль codecs здесь. Если вам нужно реализовать совершенно новую кодировку, вам придётся изучить интерфейсы модуля codecs, но реализация кодировок – специализированная задача, которая также не будет рассмотрена здесь. Обратитесь к документации Python, чтобы узнать больше об этом модуле.

Литералы Unicode в исходном коде PythonUnicode 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 для получения дополнительной информации.

Свойства UnicodeUnicode 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

Коды категорий – это сокращения, описывающие природу символа. Они сгруппированы в категории, такие как «Letter» (Буква), «Number» (Число), «Punctuation» (Знак препинания) или «Symbol» (Символ), которые в свою очередь разбиваются на подкатегории. Взяв коды из приведённого выше вывода, 'Ll' означает «Letter, lowercase» (Буква, строчная), 'No' означает «Number, other» (Число, прочее), 'Mn' – «Mark, nonspacing» (Знак, некомбинирующий), а 'So' – «Symbol, other» (Символ, прочее). См. <http://www.unicode.org/reports/tr44/#General_Category_Values> для списка кодов категорий.

СсылкиReferences

Тип str описан в справочнике по библиотеке Python в разделе Sequence Types – str, bytes, bytearray, list, tuple, range.

Документация модуля unicodedata.

Документация модуля codecs.

Марк-Андре Лембург сделал доклад на EuroPython 2002 под названием «Python and Unicode». PDF-версия его слайдов доступна по адресу <http://downloads.egenix.com/python/Unicode-EPC2002-Talk.pdf> и представляет собой отличный обзор дизайна возможностей Python для работы с Unicode (основанный на Python 2, где тип строк Unicode называется unicode, а литералы начинаются с u).

Чтение и запись данных UnicodeReading and Writing Unicode Data

После написания кода, работающего с данными Unicode, следующая проблема – ввод/вывод. Как передавать строки Unicode в программу и как преобразовывать Unicode в форму, подходящую для хранения или передачи?

Возможно, вам может не понадобиться ничего делать в зависимости от источников ввода и мест назначения вывода; следует проверить, поддерживают ли библиотеки, используемые в вашем приложении, Unicode нативно. Например, XML-парсеры часто возвращают данные в Unicode. Многие реляционные базы данных также поддерживают столбцы со значением Unicode и могут возвращать значения Unicode из SQL-запросов.

Данные Unicode обычно преобразуются в определённую кодировку перед тем, как записываются на диск или отправляются через сокет. Можно выполнить всю работу самостоятельно: открыть файл, прочитать 8-битный объект bytes и преобразовать строку с помощью bytes.decode(encoding). Однако ручной подход не рекомендуется.

Одна из проблем – многобайтовая природа кодировок: один символ Unicode может быть представлен несколькими байтами. Если вы хотите читать файл частями произвольного размера (скажем, по 1 КБ или 4 КБ), нужно написать код обработки ошибок для случаев, когда в конце части считывается только часть байтов, кодирующих один символ Unicode. Одно из решений – прочитать весь файл в память, а затем выполнить декодирование, но это не позволяет работать с очень большими файлами; если нужно прочитать файл размером 2 ГБ, потребуется 2 ГБ ОЗУ. (Даже больше, на самом деле, поскольку хотя бы на мгновение в памяти должны находиться и закодированная строка, и её версия в Unicode.)

Решением было бы использование низкоуровневого интерфейса декодирования для обработки случаев частичных кодировочных последовательностей. Работа по реализации этого уже выполнена за вас: встроенная функция open() может возвращать объект, подобный файловому, который предполагает, что содержимое файла находится в указанной кодировке, и принимает параметры Unicode для таких методов, как read() и write(). Это работает благодаря параметрам encoding и errors функции open(), которые интерпретируются так же, как и в str.encode() и bytes.decode().

Чтение Unicode из файла, таким образом, просто:

with open('unicode.rst', 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]))

Символ Юникода 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', чтобы автоматически пропускать метку, если она присутствует, при чтении таких файлов.

Имена файлов в UnicodeUnicode filenames

Большинство распространённых сегодня операционных систем поддерживают имена файлов, содержащие произвольные символы Unicode. Обычно это реализуется преобразованием строки Unicode в некоторую кодировку, которая зависит от системы. Например, Mac OS X использует UTF-8, тогда как Windows использует настраиваемую кодировку; в Windows Python использует имя «mbcs» для обозначения текущей настроенной кодировки. В системах Unix кодировка файловой системы будет доступна только при установке переменных среды LANG или LC_CTYPE; если они не установлены, кодировка по умолчанию – ASCII.

Функция sys.getfilesystemencoding() возвращает кодировку, используемую в текущей системе, на случай, если понадобится выполнить кодирование вручную, но обычно в этом нет особой необходимости. При открытии файла для чтения или записи можно просто передать строку Юникода в качестве имени файла, и она будет автоматически преобразована в нужную кодировку.

filename = 'filename\u4500abc'
with open(filename, 'w') as f:
    f.write('blah\n')

Функции из модуля os, такие как os.stat(), также принимают имена файлов в Юникоде.

Функция os.listdir(), возвращающая имена файлов, порождает вопрос: должна ли она возвращать версию имён файлов в Unicode или должна возвращать bytes, содержащие закодированные версии? os.listdir() будет делать и то, и другое в зависимости от того, предоставили ли вы путь к каталогу как bytes или строку Unicode. Если передать строку Unicode в качестве пути, имена файлов будут декодированы с использованием кодировки файловой системы и будет возвращён список строк Unicode, тогда как передача пути в виде bytes вернёт байтовые версии имён файлов. Например, предполагая, что кодировка файловой системы по умолчанию – 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'.svn', b'filename\xe4\x94\x80abc', ...]
['.svn', 'filename\u4500abc', ...]

Первый список содержит имена файлов в кодировке UTF-8, а второй список – версии в Unicode.

Обратите внимание, что в большинстве случаев следует использовать API Unicode. API bytes следует использовать только в системах, где могут встречаться не декодируемые имена файлов, т.е. в системах Unix.

Советы по написанию программ с поддержкой UnicodeTips for Writing Unicode-aware Programs

В этом разделе приводятся некоторые рекомендации по написанию программного обеспечения, работающего с Unicode.

Самый важный совет:

Программное обеспечение должно работать внутри только со строками Unicode, декодируя входные данные как можно раньше и кодируя вывод только в конце.

Если попытаться написать функции обработки, которые принимают как строки Юникода, так и байтовые строки, программа будет уязвима для ошибок в местах, где используются строки разных типов. Автоматического кодирования или декодирования не происходит: например, при попытке выполнить str + bytes будет возбуждено исключение TypeError.

При использовании данных из веб-браузера или другого ненадёжного источника распространённый приём – проверка строки на недопустимые символы перед использованием в генерируемой командной строке или сохранением в базе данных. Если это делается, следует проверять декодированную строку, а не закодированные байтовые данные; некоторые кодировки могут обладать интересными свойствами, такими как отсутствие биективности или полной совместимости с ASCII. Это особенно важно, если входные данные также задают кодировку, так как злоумышленник может выбрать хитрый способ скрыть вредоносный текст в закодированном потоке байтов.

СсылкиReferences

PDF-слайды презентации Марка-Андре Лембурга «Writing Unicode-aware Applications in Python» доступны по адресу <http://downloads.egenix.com/python/LSM2005-Developing-Unicode-aware-applications-in-Python.pdf> и обсуждают вопросы кодировок символов, а также то, как интернационализировать и локализовать приложение. Эти слайды охватывают только Python 2.x.

БлагодарностиAcknowledgements

Благодарности следующим людям, которые указали на ошибки или предложили улучшения для этой статьи: Nicholas Bastin, Marius Gedminas, Kent Johnson, Ken Krugler, Marc-André Lemburg, Martin von Löwis, Chad Whitacre.