Unicode HOWTO

Релиз:1.12

Этот HOWTO обсуждает Python’s поддержку спецификации юникод для представления текстовых данных и объясняет различные проблемы, с которыми люди обычно сталкиваются при работе с юникодом.

Введение в Unicode

Определения

Сегодняшние программы должны уметь обращаться с самыми разнообразными символами. Приложения часто интернационализированы для отображения сообщений и вывода на различных выбранных пользователем языках; одной и той же программе может потребоваться вывести сообщение об ошибке на английском, французском, японском, иврите или русском языках. Веб-контент может быть написан на любом из этих языков, а также может включать в себя множество эмодзи символов. Тип Python’s строка использует стандарт Unicode для представления знаков, который позволяет программам Python работать со всеми этими различными возможными знаками.

Unicode (https://www.unicode.org/) - спецификация, целью которой является перечисление каждого символ используемый по человеческим языкам и придание каждому символ своего уникального код. Спецификации юникода постоянно пересматриваются и обновляются для добавления новых языков и символов.

Символ является самым маленьким компонентом текста. „A“, „B“, „C“ и т.д. являются разными символами. Так „È“ и „Í“. Символы различаются в зависимости от языка или контекст, о которых вы говорите. Например, есть символ для «Первой римской цифры», „Ⅰ“, которая отделена от прописной буквы „I“. Обычно они выглядят одинаково, но это два разных символа, имеющих разные значения.

Стандарт юникод описывает, как символы представлены в кодовые точки. Значение кодовой точки представляет собой целое число в диапазоне от 0 до 0x10FFFF (около 1,1 миллиона значений, при этом до сих пор назначено около 110 тысяч). В стандарте и в этом документе точка код записывается с помощью обозначения U+265E для обозначения символ со значением 0x265e (9822 в десятичном формате).

Стандарт юникод содержит множество таблиц, содержащих символы и соответствующие им кодовые точки:

0061    'a'; LATIN SMALL LETTER A
0062    'b'; LATIN SMALL LETTER B
0063    'c'; LATIN SMALL LETTER C
...
007B    '{'; LEFT CURLY BRACKET
...
2167    'Ⅷ'; ROMAN NUMERAL EIGHT
2168    'Ⅸ'; ROMAN NUMERAL NINE
...
265E    '♞'; BLACK CHESS KNIGHT
265F    '♟'; BLACK CHESS PAWN
...
1F600   '😀'; GRINNING FACE
1F609   '😉'; WINKING FACE
...

Строго, эти определения подразумевают, что бессмысленно сказать, что „это - символ U+265E“. U+265E - точка код, которая представляет некоторый конкретный символ; в данном случае он представляет собой символ „ЧЁРНЫЙ ШАХМАТНЫЙ КОНЬ“, „♞“. В неофициальном контексте это различие между код точками и символами иногда будет забыто.

Символ представлен на экране или на бумаге набором графических элементов, называемых глиф. Глиф для верхнего регистра а, например, представляет собой два диагональных штриха и горизонтальный штрих, хотя точные детали будут зависеть от шрифта, который должен быть используемый. Большинству Python код не нужно беспокоиться о глифах; определение правильного глифа для отображения, как правило, является заданием набора инструментов GUI или средства визуализации шрифтов терминала.

Кодировка

Суммировать предыдущий раздел: Unicode строка - последовательность точек код, которые являются числами от 0 до 0x10FFFF (1 114 111 десятичных чисел). Эта последовательность точек код должна быть представлена в памяти как набор кодовые единицы и кодовые единицы затем отображаются на 8-битные байты. Правила для перевода строки Unicode в последовательность байтов называются кодировка символов или просто кодировка.

Первый кодировка, о котором вы могли бы думать, использует 32-битные целые числа в качестве единицы код и затем использует представление центрального процессора 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. Увеличение использования ОЗУ не имеет большого значения (настольные компьютеры имеют гигабайты ОЗУ, а строки обычно не так велики), но расширение использования дисков и пропускной способности сети в 4 раза недопустимо.
  3. Это не совместимо с существующими функциями C, такими как strlen(), таким образом, новая семья широких функций строка должна была бы быть используемый.

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

UTF-8 - один из обычно кодирование используемый и Python часто дефолты к использованию его. UTF означает «формат преобразования юникода», а «8» означает, что 8-битные значения являются используемый в кодировка. (Есть также кодировки UTF-16 и UTF-32, но они реже используемый, чем UTF-8.) UTF-8 использует следующие правила:

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

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

  1. Это может обработать любую точку Unicode код.
  2. Unicode строка превращен в пос ледовательность байтов, которая содержит вложенные нулевые байты только там, где они представляют пустой символ (U 0000). Это означает, что UTF-8 строки может обрабатываться C-функциями, такими как strcpy(), и отправляться через протоколы, которые не могут обрабатывать ноль байт для чего- либо, кроме маркеров end-of-строка.
  3. строка текста ASCII - также действительный текст UTF-8.
  4. UTF-8 довольно компактна; большинство обычно знаков используемый может быть представлено с одним или двумя байтами.
  5. Если байты повреждены или потеряны, можно определить начало следующей точки UTF-8-кодированный код и выполнить повторную синхронизацию. Также маловероятно, что случайные 8-битные данные будут выглядеть как действительные UTF-8.
  6. UTF-8 является байт-ориентированным кодировка. Параметр кодировка указывает, что каждый символ представлен определенной последовательностью из одного или нескольких байтов. Это избегает проблем порядка байт, которые могут произойти с целым числом, и слово ориентировало кодировках, как UTF-16 и UTF-32, где последовательность байтов варьируется в зависимости от аппаратных средств, на которых строка был кодированный.

Ссылки

У Сайт Консорциума Юникод есть диаграммы символ, глоссарий и версии PDF спецификации Unicode. Будьте готовы к трудному чтению. Хронология происхождения и разработки юникода также имеется на сайте.

На канале Computerphile Youtube Том Скотт кратко обсуждает история Unicode и UTF-8 (9 минут 36 секунд).

Чтобы помочь понять стандарт, Юкка Корпела написал вводное руководство чтению таблиц Unicode символ.

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

Статьи википедии часто полезны; см., например, записи для «кодировка символов» и UTF-8, например.

Поддержка Unicode Python’ом

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

Тип строки

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

кодировка по умолчанию для источника Python, код - UTF-8, таким образом, вы можете просто включать Unicode символ в литерал строка:

try:
    with open('/tmp/input.txt', 'r') as f:
        ...
except OSError:
    # 'Файл не найден' сообщить об ошибке.
    print("Fichier non trouvé")

Примечание: Python 3 также поддерживает использование символов юникода в идентификаторах:

répertoire = "/tmp/records.log"
with open(répertoire, "w") as f:
    f.write("test\n")

Если вы не можете войти в особый символ в своего редактора или хотеть сохранить источник код только для ASCII по некоторым причинам, вы можете также использовать последовательности побега в опечатках строка. (В зависимости от вашей системы, вы можете увидеть фактический глиф capital-delta вместоu escape.):

>>> "\N{GREEK CAPITAL LETTER DELTA}"  # Using the character name
'\u0394'
>>> "\u0394"                          # Using a 16-bit hex value
'\u0394'
>>> "\U00000394"                      # Using a 32-bit hex value
'\u0394'

Кроме того, можно создать строка с помощью decode() метод bytes. Этот метод берет аргумент encoding, такой как UTF-8 и произвольно аргумент errors.

Аргумент errors определяет ответ, когда вход строка не может быть преобразован согласно правилам кодирования. Допустимые значения для этого аргумента: 'strict' (вызвать исключение UnicodeDecodeError), 'replace' (использовать U+FFFD, REPLACEMENT CHARACTER), 'ignore' (просто оставить символ вне результата юникода) или 'backslashreplace' (вставляет последовательность \xNN escape). Следующие примеры показывают различия:

>>> b'\x80abc'.decode("utf-8", "strict")  
Traceback (most recent call last):
    ...
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0:
  invalid start byte
>>> b'\x80abc'.decode("utf-8", "replace")
'\ufffdabc'
>>> b'\x80abc'.decode("utf-8", "backslashreplace")
'\\x80abc'
>>> b'\x80abc'.decode("utf-8", "ignore")
'abc'

Кодировки указываются как строки, содержащие имя кодировки. Python поставляется с примерно 100 различными кодировками; см. Ссылку библиотеки Python в Стандартные кодировки для списка. Некоторые кодировки имеют несколько названий; например, 'latin-1', 'iso_8859_1' и '8859 „все являются синонимами для одного и того же кодировка.

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

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

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

метод противоположного bytes.decode() - str.encode(), который возвращает представление bytes последовательности Unicode, кодированный в запрошенный encoding.

Параметр errors совпадает с параметром decode() метод, но поддерживает еще несколько возможных обработчиков. А также 'strict', 'ignore' и 'replace' (который в этом случае вставляет вопросительный знак вместо юникодного символа), есть также 'xmlcharrefreplace' (вставляет ссылку XML символ), backslashreplace (вставляет экранированную последовательность \uNNNN), и namereplace (вставляет экранированную последовательность \N{...}).

В следующем примере показаны различные результаты:

>>> u = chr(40960) + 'abcd' + chr(1972)
>>> u.encode('utf-8')
b'\xea\x80\x80abcd\xde\xb4'
>>> u.encode('ascii')  
Traceback (most recent call last):
    ...
UnicodeEncodeError: 'ascii' codec can't encode character '\ua000' in
  position 0: ordinal not in range(128)
>>> u.encode('ascii', 'ignore')
b'abcd'
>>> u.encode('ascii', 'replace')
b'?abcd?'
>>> u.encode('ascii', 'xmlcharrefreplace')
b'&#40960;abcd&#1972;'
>>> u.encode('ascii', 'backslashreplace')
b'\\ua000abcd\\u07b4'
>>> u.encode('ascii', 'namereplace')
b'\\N{YI SYLLABLE IT}abcd\\u07b4'

Установленный порядок низкоуровневое для регистрации и доступа к доступному кодированию найден в модуле codecs. Внедрение новых кодировок также требует понимания модуля codecs. Однако функции кодировка и декодирования, возвращаемые этим модулем, обычно более низкоуровневое, чем удобно, и запись новых кодировок является специализированной задачей, поэтому модуль не будет охватываться этим HOWTO.

Литералы Unicode в исходном коде Python

В исходном коде Python определенные точки Unicode код могут быть написаны, используя последовательность побега \u, которая сопровождается четырьмя шестнадцатеричными цифрами, дающими точку код. Последовательность побега \U подобна, но ожидает восемь шестнадцатеричных цифр, не четыре:

>>> s = "a\xac\u1234\u20ac\U00008000"
... #     ^^^^ two-digit hex escape
... #         ^^^^^^ four-digit Unicode escape
... #                     ^^^^^^^^^^ eight-digit Unicode escape
>>> [ord(c) for c in s]
[97, 172, 4660, 8364, 32768]

Использование escape последовательностей для код точек больше 127 хорошо в малых дозах, но становится раздражением, если вы используете много акцентированных символов, как вы бы в программе с сообщениями на французском или каком-то другом языке использования акцента. Вы также можете собрать строки с помощью встроенной функции chr(), но это еще более утомительно.

Идеально, вы хотели бы написать опечатки в естественном кодировка своего языка. Затем вы можете редактировать Python source код с помощью любимого редактора, который будет отображать символы с акцентом естественно, и иметь правильные символы используемый во время выполнения.

По умолчанию Python поддерживает запись источника код в UTF-8, но можно использовать почти любой кодировка, если объявить кодировка используемый. Это выполняется путем включения специального комментария в качестве первой или второй строки исходного файла:

#!/usr/bin/env python
# -*- coding: latin-1 -*-

u = 'abcdé'
print(ord(u[-1]))

Синтаксис вдохновлен примечанием эмакса для определения переменных локальная к файлу. Emacs поддерживает множество различных переменных, но Python поддерживает только «кодирование». Символы -*- указывают компании Emacs, что комментарий является особенным; они не имеют никакого значения для Python, но являются конвенцией. Python ищет coding: name или coding=name в комментарии.

Если вы не будете включать такой комментарий, то кодировка используемый по умолчанию будет UTF-8, как уже упомянуто. Для получения дополнительной информации см. также раздел PEP 263.

Свойства Unicode

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

В следующей программе отображается некоторая информация о нескольких символах и печатается числовое значение одного конкретного символа:

import unicodedata

u = chr(233) + chr(0x0bf2) + chr(3972) + chr(6000) + chr(13231)

for i, c in enumerate(u):
    print(i, '%04x' % ord(c), unicodedata.category(c), end=" ")
    print(unicodedata.name(c))

# Получить числовое значение второго символа
print(unicodedata.numeric(u[1]))

При запуске это печатает:

0 00e9 Ll LATIN SMALL LETTER E WITH ACUTE
1 0bf2 No TAMIL NUMBER ONE THOUSAND
2 0f84 Mn TIBETAN MARK HALANTA
3 1770 Lo TAGBANWA LETTER SA
4 33af So SQUARE RAD OVER S SQUARED
1000.0

Категория коды представляет собой сокращения, описывающие характер символ. Они сгруппированы в такие категории, как «Буква», «Число», «Пунктуация» или «Символ», которые в свою очередь разбиваются на подкатегории. Для получения коды из указанного выше вывода, 'Ll' означает «Буква, строчный регистр», 'No' означает «Число, другое», 'Mn' означает «Маркировать, не пространственно», а 'So' означает «Символ, другое». Список категорий раздел Общие значения категорий документации по базе данных символов Unicode см. в разделе коды.

Сравнение строк

Юникод добавляет некоторое усложнение к сравнению строк, поскольку один и тот же набор символов может быть представлен различными последовательностями код точек. Например, буква типа «к» может быть представлена как единственная точка код U+00EA, или как U+0065 U+0302, которая является точкой код для «е», за которой следует точка код для «комбинирования акцента CIRCUMFLEX». Они будут выдавать те же выходные данные при печати, но один является строка длины 1, а другой - длиной 2.

Одним из инструментов для сравнения без учета регистра является casefold() строка метод, который преобразует строка в форму без учета регистра в соответствии с алгоритмом, описанным в стандарте юникод. Этот алгоритм имеет специальную обработку для символов, таких как немецкая буква „s“ (точка код U+00DF), которая становится парой строчных букв „ss“.

>>> street = 'Gürzenichstraße'
>>> street.casefold()
'gürzenichstrasse'

Второй инструмент - функция normalize() модуля unicodedata, которая преобразовывает строки в одну из нескольких нормальных форм, где письма, сопровождаемые объединением символ, заменены единственными знаками. normalize() может быть используемый для выполнения сравнений строка, которые не будут ложно сообщать о неравенстве, если два строки используют объединение символов по-разному:

import unicodedata

def compare_strs(s1, s2):
    def NFD(s):
        return unicodedata.normalize('NFD', s)

    return NFD(s1) == NFD(s2)

single_char = 'ê'
multiple_chars = '\N{LATIN SMALL LETTER E}\N{COMBINING CIRCUMFLEX ACCENT}'
print('length of first string=', len(single_char))
print('length of second string=', len(multiple_chars))
print(compare_strs(single_char, multiple_chars))

При выполнении это выводит:

$ python3 compare-strs.py
length of first string= 1
length of second string= 2
True

Первым аргументом функции normalize() является строка, дающий желаемую форму нормализации, которая может быть одной из „NFC“, „NFKC“, „NFD“ и „NFKD“.

Стандарт юникода также определяет способ безрегистрового сравнения:

import unicodedata

def compare_caseless(s1, s2):
    def NFD(s):
        return unicodedata.normalize('NFD', s)

    return NFD(NFD(s1).casefold()) == NFD(NFD(s2).casefold())

# Пример использования
single_char = 'ê'
multiple_chars = '\N{LATIN CAPITAL LETTER E}\N{COMBINING CIRCUMFLEX ACCENT}'

print(compare_caseless(single_char, multiple_chars))

Это будет печатать True. (Почему NFD() вызывается дважды? поскольку существует несколько символов, которые заставляют casefold() возвращать ненормализованную строку, результат необходимо нормализовать снова. Обсуждение и пример см. в разделе 3.13 стандарта юникод.)

Регулярные выражения Unicode

Регулярные выражения, поддерживаемые модулем re, могут быть предоставлены в байтах или строки. Некоторые специальные последовательности символ, такие как \d и \w, имеют различное значение в зависимости от того, подается ли шаблон в виде байтов или строка. Например, \d будет соответствовать символам [0-9] в байтах, но в строки будет соответствовать любому символ, который находится в категории 'Nd'.

строка в этом примере имеет число 57, написанное как тайскими, так и арабскими цифрами:

import re
p = re.compile(r'\d+')

s = "Over \u0e55\u0e57 57 flavours"
m = p.search(s)
print(repr(m.group()))

При выполнении \d+ будет совпадать с тайскими цифрами и распечатывать их. Если вы будете поставлять флаг re.ASCII compile(), то \d+ будет соответствовать подстроке «57» вместо этого.

Аналогично, \w соответствует широкому спектру символов юникода, но только [a-zA-Z0-9_] в байтах или если задан параметр re.ASCII, и \s будет соответствовать символам пробела юникода или [ \t\n\r\f\v].

Ссылки

Некоторые хорошие альтернативные обсуждения Python Unicode поддерживают:

Тип str описан в справочнике библиотеки Python по адресу Тип последовательности текста — str.

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

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

Марк-Андре Лембург предоставил презентацию под названием «Python и Unicode» (PDF слайды) на EuroPython 2002. Слайды - превосходный обзор дизайна особенностей Python 2’s Unicode (где тип Unicode строка называют unicode, и опечатки начинаются с u).

Чтение и запись данных в юникоде

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

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

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

Одной из проблем является многобайтовая природа кодировок; один Unicode символ может быть представлен на несколько байтов. Если вы хотите читать, файл в чанки произвольного размера (скажем, 1024 или 4 096 байтов), вы должны написать обработке ошибок код, чтобы поймать случай, где только часть байтов кодировка единственный Unicode символ прочитана в конце чанк. Одним из решений было бы считывание всего файла в память и последующее выполнение декодирования, но это не позволяет работать с файлами, которые являются чрезвычайно большими; если необходимо прочитать файл 2 GiB, необходимо 2 GiB оперативной памяти. (Более того, действительно, так как по крайней мере на мгновение вам нужно будет иметь в памяти и кодированный строка, и его версию юникод.

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

Поэтому чтение юникода из файла является простым:

with open('unicode.txt', encoding='utf-8') as f:
    for line in f:
        print(repr(line))

Также можно открывать файлы в режиме обновления, позволяя как чтение, так и запись:

with open('test', encoding='utf-8', mode='w+') as f:
    f.write('\u4500 blah blah blah\n')
    f.seek(0)
    print(repr(f.readline()[:1]))

Unicode символ U+FEFF - используемый как отметка порядка байтов (BOM) и часто пишется как первый символ файла, чтобы помочь с автообнаружением заказа байта файла. Некоторые кодировки, такие как UTF-16, ожидают присутствия ведомости материалов в начале файла; когда такой кодировка будет использоваться, BOM будет автоматически написан как первый символ и будет тихо пропущен, когда файл прочитан. Существуют варианты этих кодировок, такие как „utf-16-le“ и „utf-16-be“ для кодировок little-endian и big-endian, которые определяют один конкретный порядок байтов и не пропускают BOM.

В некоторых областях также рекомендуется использовать «BOM» в начале файлов UTF-8 кодированный; имя вводит в заблуждение, поскольку UTF-8 не зависит от порядка байтов. Отметка просто объявляет, что файл - кодированный в UTF-8. Для чтения таких файлов используйте кодировка «utf-8-sig», чтобы автоматически пропустить метку при ее наличии.

Имена файлов Unicode

Большинство операционных систем, используемых в настоящее время, поддерживают имена файлов, содержащие произвольные символы юникода. Обычно это осуществлено, преобразовав Unicode строка в некоторый кодировка, который варьируется в зависимости от системы. Сегодня Python сходится на использовании UTF-8: Python на MacOS имеет используемый UTF-8 для нескольких версий, и Python 3.6 переключился на использование UTF-8 на Windows. На системах Unix только будет файловая система кодировка, если вы установили LANG или переменные окружения LC_CTYPE; если вы не имеете, дефолт, кодировка - снова UTF-8.

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

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

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

Функция os.listdir() возвращает имена файлов, что вызывает проблему: должна ли она возвращать юникод-версию имен файлов, или должна возвращать байты, содержащие версии кодированный? os.listdir() может сделать обоих, в зависимости от того, обеспечили ли вы путь к директории как байты или Unicode строка. Если вы передадите Unicode строка как путь, то имена файлов будут расшифрованы, используя кодировка файловой системы, и список Unicode строки будет возвращен, в то время как прохождение пути байта возвратит имена файлов как байты. Например, при условии, что файловая система по умолчанию кодировка имеет значение UTF-8, выполняется следующая программа:

fn = 'filename\u4500abc'
f = open(fn, 'w')
f.close()

import os
print(os.listdir(b'.'))
print(os.listdir('.'))

выведет следующие выходные данные:

$ python listdir-test.py
[b'filename\xe4\x94\x80abc', ...]
['filename\u4500abc', ...]

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

Следует отметить, что в большинстве случаев с помощью этих API можно просто использовать юникод. Байты API должны быть используемый только в системах, где могут присутствовать недекодируемые имена файлов; это практически только системы Unix.

Советы по написанию программ с поддержкой юникода

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

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

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

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

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

Преобразование между кодировками файлов

StreamRecoder класс может прозрачно преобразовывать между кодировками, принимая поток, который возвращает данные в кодировка # 1, и вести себя как поток, возвращающий данные в кодировка # 2.

Например, если у вас есть входной файл f, который находится в Latin-1, вы можете обернуть его StreamRecoder, чтобы вернуть байты кодированный в UTF-8:

new_f = codecs.StreamRecoder(f,
    # en/декодер: используется для read() чтобы закодировать его результаты и
    # write() для декдирования ввода.
    codecs.getencoder('utf-8'), codecs.getdecoder('utf-8'),

    # читатель/писатель: используется для чтения и записи в поток.
    codecs.getreader('latin-1'), codecs.getwriter('latin-1') )

Файлы в неизвестной кодировке

Что вы можете сделать, если вам нужно изменить файл, но не знаете кодировку файла? если вы знаете, что кодировка совместим с ASCII, и только хотят исследовать или изменить части ASCII, вы можете открыть файл с ошибочным обработчиком surrogateescape:

with open(fname, 'r', encoding="ascii", errors="surrogateescape") as f:
    data = f.read()

# внесите изменения в строку 'data'

with open(fname + '.new', 'w',
          encoding="ascii", errors="surrogateescape") as f:
    f.write(data)

Обработчик ошибок surrogateescape декодирует любые байты, не являющиеся байтами ASCII, как точки код в специальном диапазоне от U+DC80 до U+DCFF. Эти точки код тогда возвратятся в те же байты, когда ошибочный укладчик surrogateescape будет используемый, чтобы закодировать данные и написать его в ответ.

Ссылки

Одна секция Освоение Ввода-Вывода Python 3, речь на PyCon 2010 Дэвидом Бизли, обсуждает текстовую обработку и обработку двоичных данных.

PDF слайды для презентации Марка-Андре Лембурга «написание Юникод-зависимых приложений на Python» обсуждают вопросы кодирования символ, а также как интернационализировать и локализовать приложение. Эти слайды покрывают Python 2.x только.

Сила Юникода в Python - это выступление Бенджамина Питерсона на PyCon 2013, в котором обсуждается внутреннее представление юникода в Python 3.3.

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

Первоначальный проект этого документа написал Эндрю Кучлинг. С тех пор он был пересмотрен Александром Белопольским, Георгом Брандлем, Эндрю Кучлингом и Эцио Мелотти.

Спасибо следующим людям, которые отметили ошибки или предложили изменения этой статьи: Éric Araujo, Nicholas Bastin, Nick Coghlan, Marius Gedminas, Kent Johnson, Ken Krugler, Marc-André Lemburg, Martin von Löwis, Terry J. Reedy, Serhiy Storchaka, Eryk Sun, Chad Whitacre, Graham Wideman.