Как открыть юникод
Перейти к содержимому

Как открыть юникод

  • автор:

Unicode HOWTO¶

Этот 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 '

Строго, эти определения подразумевают, что бессмысленно сказать, что „это - символ 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 " # 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'ꀀabcd޴' >>> u.encode('ascii', 'backslashreplace') b'\\ua000abcd\\u07b4' >>> u.encode('ascii', 'namereplace') b'\\Nabcd\\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\N ' 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\N ' 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 поддерживают:

  • Обработка текстовых файлов в Python 3, Ник Коглан.
  • Прагматичный Юникод, презентация PyCon 2012 Недом Батчелдером.

Тип 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.

Содержание

  • Unicode HOWTO
    • Введение в Unicode
      • Определения
      • Кодировка
      • Ссылки
      • Тип строки
      • Преобразование в байты
      • Литералы Unicode в исходном коде Python
      • Свойства Unicode
      • Сравнение строк
      • Регулярные выражения Unicode
      • Ссылки
      • Имена файлов Unicode
      • Советы по написанию программ с поддержкой юникода
        • Преобразование между кодировками файлов
        • Файлы в неизвестной кодировке

        Как открыть файл в браузере, имя которого содержит unicode?

        На сервере сохранен файл с именем name-%e2%84%96-112-100g__0-200x150.jpg
        При попытки открытия в браузере название подменяется на name-№-112-100g__0-200x150.jpg и nginx отдает 404.
        Есть ли способ корректно открыть в браузере такой файл, не переименовывая его на сервере?

        • Вопрос задан более трёх лет назад
        • 43 просмотра

        Комментировать

        Решения вопроса 1

        Естественно - нужно просто заменить в URL символы % на их URL-encoded эквивалент %25 . Декодирование URL - не рекурсивно, потому, при декодировании это превратится обратно в % .

        Ответ написан более трёх лет назад

        Комментировать

        Нравится 1 Комментировать

        Ответы на вопрос 0

        Ваш ответ на вопрос

        Войдите, чтобы написать ответ

        wordpress

        • WordPress
        • +2 ещё

        После удаления SSL на сайте просто белый экран, почему?

        • 1 подписчик
        • вчера
        • 53 просмотра

        Юникод для чайников

        logo

        Сам я не очень люблю заголовки вроде «Покемоны в собственном соку для чайников\кастрюль\сковородок», но это кажется именно тот случай — говорить будем о базовых вещах, работа с которыми довольно часто приводить к купе набитых шишек и уйме потерянного времени вокруг вопроса — «Почему же оно не работает?». Если вы до сих пор боитесь и\или не понимаете Юникода — прошу под кат.

        Зачем?

        Главный вопрос новичка, который встречается с впечатляющим количеством кодировок и на первый взгляд запутанными механизмами работы с ними (например, в Python 2.x). Краткий ответ — потому что так сложилось 🙂

        Кодировкой, кто не знает, называют способ представления в памяти компьютера (читай — в нулях-единицах\числах) цифр, буков и всех остальных знаков. Например, пробел представляется как 0b100000 (в двоичной), 32 (в десятичной) или 0x20 (в шестнадцатеричной системе счисления).

        Так вот, когда-то памяти было совсем немного и всем компьютерам было достаточно 7 бит для представления всех нужных символов (цифры, строчный\прописной латинский алфавит, куча знаков и так называемые управляемые символы — все возможные 127 номеров были кому-то отданы). Кодировка в это время была одна — ASCII. Шло время, все были счастливы, а кто не был счастлив (читай — кому не хватало знака "©" или родной буквы «щ») — использовали оставшиеся 128 знаков на свое усмотрение, то есть создавали новые кодировки. Так появились и ISO-8859-1, и наши (то есть кириличные) cp1251 и KOI8. Вместе с ними появилась и проблема интерпретации байтов типа 0b1******* (то есть символов\чисел от 128 и до 255) — например, 0b11011111 в кодировке cp1251 это наша родная «Я», в тоже время в кодировке ISO-8859-1 это греческая немецкая Eszett (подсказывает Moonrise) "ß". Ожидаемо, сетевая коммуникация и просто обмен файлами между разными компьютерами превратились в чёрт-знает-что, несмотря на то, что заголовки типа 'Content-Encoding' в HTTP протоколе, email-письмах и HTML-страницах немного спасали ситуацию.

        В этот момент собрались светлые умы и предложили новый стандарт — Unicode. Это именно стандарт, а не кодировка — сам по себе Юникод не определяет, как символы будут сохранятся на жестком диске или передаваться по сети. Он лишь определяет связь между символом и некоторым числом, а формат, согласно с которым эти числа будут превращаться в байты, определяется Юникод-кодировками (например, UTF-8 или UTF-16). На данный момент в Юникод-стандарте есть немного более 100 тысяч символов, тогда как UTF-16 позволяет поддерживать более одного миллиона (UTF-8 — и того больше).

        Ближе к делу!

        Естественно, есть поддержка Юникода и в Пайтоне. Но, к сожалению, только в Python 3 все строки стали юникодом, и новичкам приходиться убиваться об ошибки типа:

        >>> with open('1.txt') as fh: s = fh.read() >>> print s кощей >>> parser_result = u'баба-яга' # присвоение для наглядности, представим себе, что это результат работы какого-то парсера >>> parser_result + s 
        Traceback (most recent call last): File "", line 1, in parser_result + s UnicodeDecodeError: 'ascii' codec can't decode byte 0xea in position 0: ordinal not in range(128) 
        >>> str(parser_result) 
        Traceback (most recent call last): File "", line 1, in str(parser_result) UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128) 

        Давайте разберемся, но по порядку.

        Зачем кто-то использует Юникод?

        Почему мой любимый html-парсер возвращает Юникод? Пусть возвращает обычную строку, а я там уже с ней разберусь! Верно? Не совсем. Хотя каждый из существующих в Юникоде символов и можно (наверное) представить в некоторой однобайтовой кодировке (ISO-8859-1, cp1251 и другие называют однобайтовыми, поскольку любой символ они кодируют ровно в один байт), но что делать если в строке должны быть символы с разных кодировок? Присваивать отдельную кодировку каждому символу? Нет, конечно, надо использовать Юникод.

        Зачем нам новый тип «unicode»?

        Вот мы и добрались до самого интересного. Что такое строка в Python 2.x? Это просто байты. Просто бинарные данные, которые могут быть чем-угодно. На самом деле, когда мы пишем что-нибудь вроде:

        >>> x = 'abcd' >>> x 'abcd' 

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

        ('a', 'b', 'c', 'd')

        с четырёх байт, и латинские буквы здесь используются исключительно для обозначения именно этого значения байта. То есть 'a' здесь просто синоним для написания '\x61', и ни чуточку больше. Например:

        >>> '\x61' 'a' >>> struct.unpack('>4b', x) # 'x' - это просто четыре signed/unsigned char-а (97, 98, 99, 100) >>> struct.unpack('>2h', x) # или два short-а (24930, 25444) >>> struct.unpack('>l', x) # или один long (1633837924,) >>> struct.unpack('>f', x) # или float (2.6100787562286154e+20,) >>> struct.unpack('>d', x * 2) # ну или половинка double-а (1.2926117739473244e+161,) 

        И ответ на вопрос — зачем нам «unicode» уже более очевиден — нужен тип, который будет представятся символами, а не байтами.

        Хорошо, я понял чем есть строка. Тогда что такое Юникод в Пайтоне?

        «type unicode» — это прежде всего абстракция, которая реализует идею Юникода (набор символов и связанных с ними чисел). Объект типа «unicode» — это уже не последовательность байт, но последовательность собственно символов без какого либо представления о том, как эти символы эффективно сохранить в памяти компьютера. Если хотите — это более высокой уровень абстракции, чем байтовый строки (именно так в Python 3 называют обычные строки, которые используются в Python 2.6).

        Как пользоваться Юникодом?
        >>> u'abc' u'abc' 
        >>> 'abc'.decode('ascii') u'abc' 
        >>> unicode('abc', 'ascii') u'abc' 
        '\x61' -> кодировка ascii -> строчная латинская "a" -> u'\u0061' (unicode-point для этой буквы) или '\xe0' -> кодировка c1251 -> строчная кириличная "a" -> u'\u0430' 

        Как из юникод-строки получить обычную? Закодировать её:

        >>> u'abc'.encode('ascii') 'abc' 

        Алгоритм кодирования естественно обратный приведенному выше.

        Запоминаем и не путаем — юникод == символы, строка == байты, и байты -> что-то значащее (символы) — это де-кодирование (decode), а символы -> байты — кодирование (encode).

        Не кодируется 🙁

        Разберем примеры с начала статьи. Как работает конкатенация строки и юникод-строки? Простая строка должна быть превращена в юникод-строку, и поскольку интерпретатор не знает кодировки, от использует кодировку по умолчанию — ascii. Если этой кодировке не удастся декодировать строку, получим некрасивую ошибку. В таком случае нам нужно самим привести строку к юникод-строке, используя правильную кодировку:

        >>> print type(parser_result), parser_result баба-яга >>> s = 'кощей' >>> parser_result + s 
        Traceback (most recent call last): File "", line 1, in parser_result + s UnicodeDecodeError: 'ascii' codec can't decode byte 0xea in position 0: ordinal not in range(128) 
        >>> parser_result + s.decode('cp1251') u'\xe1\xe0\xe1\xe0-\xff\xe3\xe0\u043a\u043e\u0449\u0435\u0439' >>> print parser_result + s.decode('cp1251') баба-ягакощей >>> print '&'.join((parser_result, s.decode('cp1251'))) баба-яга&кощей # Так лучше :) 

        «UnicodeDecodeError» обычно есть свидетельством того, что нужно декодировать строку в юникод, используя правильную кодировку.

        Теперь использование «str» и юникод-строк. Не используйте «str» и юникод строки 🙂 В «str» нет возможности указать кодировку, соответственно кодировка по умолчанию будет использоваться всегда и любые символы > 128 будут приводить к ошибке. Используйте метод «encode»:

        >>> print type(s), s кощей >>> str(s) 
        Traceback (most recent call last): File "", line 1, in str(s) UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)
        >>> s = s.encode('cp1251') >>> print type(s), s кощей

        «UnicodeEncodeError» — знак того, что нам нужно указать правильную кодировку во время превращения юникод-строки в обычную (или использовать второй параметр 'ignore'\'replace'\'xmlcharrefreplace' в методе «encode»).

        Хочу ещё!

        Хорошо, используем бабу-ягу из примера выше ещё раз:

        >>> parser_result = u'баба-яга' #1 >>> parser_result u'\xe1\xe0\xe1\xe0-\xff\xe3\xe0' #2 >>> print parser_result áàáà-ÿãà #3 >>> print parser_result.encode('latin1') #4 баба-яга >>> print parser_result.encode('latin1').decode('cp1251') #5 баба-яга >>> print unicode('баба-яга', 'cp1251') #6 баба-яга 
        1. Что имеем на входе? Байты, которые IDLE передает интерпретатору. Что нужно на выходе? Юникод, то есть символы. Осталось байты превратить в символы — но ведь надо кодировку, правда? Какая кодировка будет использована? Смотрим дальше.
        2. Здесь важной момент:
        >>> 'баба-яга' '\xe1\xe0\xe1\xe0-\xff\xe3\xe0' >>> u'\u00e1\u00e0\u00e1\u00e0-\u00ff\u00e3\u00e0' == u'\xe1\xe0\xe1\xe0-\xff\xe3\xe0' True 

        как видим, Пайтон не заморачивается с выбором кодировки — байты просто превращаются в юникод-поинты:

        >>> ord('а') 224 >>> ord(u'а') 224 
        >>> parser_result.encode('latin1') '\xe1\xe0\xe1\xe0-\xff\xe3\xe0' 
        >>> parser_result.encode('latin1').decode('cp1251') u'\u0431\u0430\u0431\u0430-\u044f\u0433\u0430' 

        Есть ещё способ использования «u''» для представления, например, кириллицы, и при этом не указывать кодировку или нечитабельные юникод-поинты (то есть «u'\u1234'»). Способ не совсем удобный, но интересный — использовать unicode entity codes:

        >>> s = u'\N\N\N\N\N' >>> print s кощей 

        Ну и вроде всё. Основные советы — не путать «encode»\«decode» и понимать различия между байтами и символами.

        Python 3

        Здесь без кода, ибо опыта нет. Свидетели утверждают, что там всё значительно проще и веселее. Кто возьмется на кошках продемонстрировать различия между здесь (Python 2.x) и там (Python 3.x) — респект и уважуха.

        Полезно

        Раз уж мы о кодировках, порекомендую ресурс, который время-от-времени помогает побороть кракозябры — http://2cyr.com/decode/?lang=ru.

        Unicode HOWTO — официальный документ о том где, как и зачем Юникод в Python 2.x.

        Спасибо за внимание. Буду благодарен за замечания в приват.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *