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Это представление является простым, но использование его представляет ряд проблем.
- Он не переносной; разные процессоры упорядочивают байты по-разному.
- Это очень расточительно для пространства. В большинстве текстов большинство точек код меньше 127, или меньше 255, поэтому много места занято 0x00 байтами. Вышеуказанное строка занимает 24 байта по сравнению с 6 байтами, необходимыми для представления ASCII. Увеличение использования ОЗУ не имеет большого значения (настольные компьютеры имеют гигабайты ОЗУ, а строки обычно не так велики), но расширение использования дисков и пропускной способности сети в 4 раза недопустимо.
- Это не совместимо с существующими функциями C, такими как strlen() , таким образом, новая семья широких функций строка должна была бы быть используемый.
Поэтому этот кодировка не используемый очень, и люди вместо этого выбирают другое кодирование, которое более эффективно и удобно, таково как UTF-8.
UTF-8 - один из обычно кодирование используемый и Python часто дефолты к использованию его. UTF означает «формат преобразования юникода», а «8» означает, что 8-битные значения являются используемый в кодировка. (Есть также кодировки UTF-16 и UTF-32, но они реже используемый, чем UTF-8.) UTF-8 использует следующие правила:
- Если точка код равна < 128, она представлена соответствующим значением байта.
- Если точка код > = 128, она превращается в последовательность из двух, трех или четырех байт, где каждый байт последовательности находится между 128 и 255.
UTF-8 обладает несколькими удобными свойствами:
- Это может обработать любую точку Unicode код.
- Unicode строка превращен в пос ледовательность байтов, которая содержит вложенные нулевые байты только там, где они представляют пустой символ (U 0000). Это означает, что UTF-8 строки может обрабатываться C-функциями, такими как strcpy() , и отправляться через протоколы, которые не могут обрабатывать ноль байт для чего- либо, кроме маркеров end-of-строка.
- строка текста ASCII - также действительный текст UTF-8.
- UTF-8 довольно компактна; большинство обычно знаков используемый может быть представлено с одним или двумя байтами.
- Если байты повреждены или потеряны, можно определить начало следующей точки UTF-8-кодированный код и выполнить повторную синхронизацию. Также маловероятно, что случайные 8-битные данные будут выглядеть как действительные UTF-8.
- 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
- Советы по написанию программ с поддержкой юникода
- Преобразование между кодировками файлов
- Файлы в неизвестной кодировке
Проблема с сохранением файла пишет что "Этот файл содержит текст в формате Юникод" подробнее внутри.
Вообщем устоновил винду хр. Создаю текст на русском, на аглийском у меня он его нормально сохраняет в блокноте. А если пишу текст латышскими буквами или какими то другими, то при сохранение выдает ету ошибку "Этот файл содержит текст в формате Юникод, который будет потерян, если вы сохраниете этот файл в формате ANSI . Что бы сохранить данные в формате Юникод, нажмите кнопку "Отмена" и выберете один из вариантов сохранения в формате Юникод. Продолжить?"
Блин но я не хочу каждый документ сохранять таким дурацким путем, это же туфта кокая та. Что делать подскажите
Aza Yuldashev
софтвилла. ру (анг. яз. ) -> поиск программы -> внутри архива есть всё, что вам нужно
В поиске наберите - notepad 2
Категория - ТекстАнна Комарова
Чтобы сохранить данные в формате Юникод: – при появлении диалогового окна с сообщением «Этот файл содержит текст в формате Юникод, который будет потерян, если вы сохраните этот файл в формате ANSI…» нажмите кнопку Отмена; – в выпадающем списке Кодировка диалогового окна "Сохранить как" выберите один из вариантов сохранения в формате Юникод (например, Юникод или UTF-8).
Unicode Encoding. Preamble Property
Some information relates to prerelease product that may be substantially modified before it’s released. Microsoft makes no warranties, express or implied, with respect to the information provided here.
Gets a Unicode byte order mark encoded in UTF-16 format, if this object is configured to supply one.
public: virtual property ReadOnlySpan Preamble < ReadOnlySpanget(); >;
public override ReadOnlySpan Preamble
member this.Preamble : ReadOnlySpan
Public Overrides ReadOnly Property Preamble As ReadOnlySpan(Of Byte)
Property Value
A byte span containing the Unicode byte order mark, if this object is configured to supply one; otherwise, the default span.
Remarks
The UnicodeEncoding object can provide a preamble, which is a byte span that can be prepended to the sequence of bytes resulting from the encoding process. Prefacing a sequence of encoded bytes with a byte order mark (code point U+FEFF ) helps the decoder determine the byte order and the transformation format or UTF. The Unicode byte order mark (BOM) is serialized as follows (in hexadecimal):
- Big endian byte order: FE FF
- Little endian byte order: FF FE
You can instantiate a UnicodeEncoding object whose Preamble is a valid BOM in the following ways:
- By retrieving the UnicodeEncoding object returned by the Encoding.Unicode or Encoding.BigEndianUnicode property.
- By calling the parameterless UnicodeEncoding() constructor to instantiate a UnicodeEncoding object.
- By supplying true as the value of the byteOrderMark argument to the UnicodeEncoding(Boolean, Boolean) or UnicodeEncoding(Boolean, Boolean, Boolean) constructors.
We recommended that you use the BOM, since it provides nearly certain identification of an encoding for files that otherwise have lost a reference to their encoding, such as untagged or improperly tagged web data or random text files stored when a business did not have international concerns. Often user problems might be avoided if data is consistently and properly tagged.
For standards that provide an encoding type, a BOM is somewhat redundant. However, it can be used to help a server send the correct encoding header. Alternatively, it can be used as a fallback in case the encoding is otherwise lost.
There are some disadvantages to using a BOM. For example, knowing how to limit the database fields that use a BOM can be difficult. Concatenation of files can be a problem also, for example, when files are merged in such a way that an unnecessary character can end up in the middle of data. In spite of the few disadvantages, however, the use of a BOM is highly recommended.
To ensure that the encoded bytes are decoded properly, you should prefix the beginning of a stream of encoded bytes with a preamble. Note that the GetBytes method does not prepend a BOM to a sequence of encoded bytes; supplying a BOM at the beginning of an appropriate byte stream is the developer's responsibility.
Как работают кодировки текста. Откуда появляются «кракозябры». Принципы кодирования. Обобщение и детальный разбор
Данная статья имеет цель собрать воедино и разобрать принципы и механизм работы кодировок текста, подробно этот механизм разобрать и объяснить. Полезна она будет тем, кто только примерно представляет, что такое кодировки текста и как они работают, чем отличаются друг от друга, почему иногда появляются не читаемые символы, какой принцип кодирования имеют разные кодировки.
Чтобы получить детальное понимание этого вопроса придется прочитать и свести воедино не одну статью и потратить довольно значительное время на это. В данном материале же это все собрано воедино и по идее должно сэкономить время и разбор на мой взгляд получился довольно подробный.
О чем будет под катом: принцип работы одно байтовых кодировок (ASCII, Windows-1251 и т.д.), предпосылки появления Unicode, что такое Unicode, Unicode-кодировки UTF-8, UTF-16, их отличия, принципиальные особенности, совместимость и несовместимость разных кодировок, принципы кодирования символов, практический разбор кодирования и декодирования.
Вопрос с кодировками сейчас конечно уже потерял актуальность, но все же знать как они работают сейчас и как работали раньше и при этом не потратить много времени на это думаю лишним не будет.
Предпосылки Unicode
Начать думаю стоит с того времени когда компьютеризация еще не была так сильно развита и только набирала обороты. Тогда разработчики и стандартизаторы еще не думали, что компьютеры и интернет наберут такую огромную популярность и распространенность. Собственно тогда то и возникла потребность в кодировке текста. В каком то же виде нужно было хранить буквы в компьютере, а он (компьютер) только единицы и нули понимает. Так была разработана одно-байтовая кодировка ASCII (скорее всего она не первая кодировка, но она наиболее распространенная и показательная, по этому ее будем считать за эталонную). Что она из себя представляет? Каждый символ в этой кодировке закодирован 8-ю битами. Несложно посчитать что исходя из этого кодировка может содержать 256 символов (восемь бит, нулей или единиц 2 8 =256).
Первые 7 бит (128 символов 2 7 =128) в этой кодировке были отданы под символы латинского алфавита, управляющие символы (такие как переносы строк, табуляция и т.д.) и грамматические символы. Остальные отводились под национальные языки. То есть получилось что первые 128 символов всегда одинаковые, а если хочешь закодировать свой родной язык пожалуйста, используй оставшуюся емкость. Собственно так и появился огромный зоопарк национальных кодировок. И теперь сами можете представить, вот например я находясь в России беру и создаю текстовый документ, у меня по умолчанию он создается в кодировке Windows-1251 (русская кодировка использующаяся в ОС Windows) и отсылаю его кому то, например в США. Даже то что мой собеседник знает русский язык, ему не поможет, потому что открыв мой документ на своем компьютере (в редакторе с дефолтной кодировкой той же самой ASCII) он увидит не русские буквы, а кракозябры. Если быть точнее, то те места в документе которые я напишу на английском отобразятся без проблем, потому что первые 128 символов кодировок Windows-1251 и ASCII одинаковые, но вот там где я написал русский текст, если он в своем редакторе не укажет правильную кодировку будут в виде кракозябр.
Думаю проблема с национальными кодировками понятна. Собственно этих национальных кодировок стало очень много, а интернет стал очень широким, и в нем каждый хотел писать на своем языке и не хотел чтобы его язык выглядел как кракозябры. Было два выхода, указывать для каждой страницы кодировки, либо создать одну общую для всех символов в мире таблицу символов. Победил второй вариант, так создали Unicode таблицу символов.
Небольшой практикум ASCII
Возможно покажется элементарщиной, но раз уж решил объяснять все и подробно, то это надо.
Вот таблица символов ASCII:
Тут имеем 3 колонки:
- номер символа в десятичном формате
- номер символа в шестнадцатиричном формате
- представление самого символа.
Unicode
С предпосылками создания общей таблицы для всех в мире символов, разобрались. Теперь собственно, к самой таблице. Unicode — именно эта таблица и есть (это не кодировка, а именно таблица символов). Она состоит из 1 114 112 позиций. Большинство этих позиций пока не заполнены символами, так что вряд ли понадобится это пространство расширять.
Разделено это общее пространство на 17 блоков, по 65 536 символов в каждом. Каждый блок содержит свою группу символов. Нулевой блок — базовый, там собраны наиболее употребляемые символы всех современных алфавитов. Во втором блоке находятся символы вымерших языков. Есть два блока отведенные под частное использование. Большинство блоков пока не заполнены.
Итого емкость символов юникода составляет от 0 до 10FFFF (в шестнадцатиричном виде).
Записываются символы в шестнадцатиричном виде с приставкой «U+». Например первый базовый блок включает в себя символы от U+0000 до U+FFFF (от 0 до 65 535), а последний семнадцатый блок от U+100000 до U+10FFFF (от 1 048 576 до 1 114 111).
Отлично теперь вместо зоопарка национальных кодировок, у нас есть всеобъемлющая таблица, в которой зашифрованы все символы которые нам могут пригодиться. Но тут тоже есть свои недостатки. Если раньше каждый символ был закодирован одним байтом, то теперь он может быть закодирован разным количеством байтов. Например для кодирования всех символов английского алфавита по прежнему достаточно одного байта например тот же символ «o» (англ.) имеет в юникоде номер U+006F, то есть тот же самый номер как и в ASCII — 6F в шестнадцатиричной и 111 в десятеричной. А вот для кодирования символа "U+103D5" (это древнеперсидская цифра сто) — 103D5 в шестнадцатиричной и 66 517 в десятеричной, тут нам потребуется уже три байта.
Решить эту проблему уже должны юникод-кодировки, такие как UTF-8 и UTF-16. Далее речь пойдет про них.
UTF-8
UTF-8 является юникод-кодировкой переменной длинны, с помощью которой можно представить любой символ юникода.
Давайте поподробнее про переменную длину, что это значит? Первым делом надо сказать, что структурной (атомарной) единицей этой кодировки является байт. То что кодировка переменной длинны, значит, что один символ может быть закодирован разным количеством структурных единиц кодировки, то есть разным количеством байтов. Так например латиница кодируется одним байтом, а кириллица двумя байтами.
Немного отступлю от темы, надо написать про совместимость ASCII и UTF
То что латинские символы и основные управляющие конструкции, такие как переносы строк, табуляции и т.д. закодированы одним байтом делает utf-кодировки совместимыми с кодировками ASCII. То есть фактически латиница и управляющие конструкции находятся на тех же самых местах как в ASCII, так и в UTF, и то что закодированы они и там и там одним байтом и обеспечивает эту совместимость.
Давайте возьмем символ «o»(англ.) из примера про ASCII выше. Помним что в таблице ASCII символов он находится на 111 позиции, в битовом виде это будет 01101111 . В таблице юникода этот символ — U+006F что в битовом виде тоже будет 01101111 . И теперь так, как UTF — это кодировка переменной длины, то в ней этот символ будет закодирован одним байтом. То есть представление данного символа в обеих кодировках будет одинаково. И так для всего диапазона символов от 0 до 128. То есть если ваш документ состоит из английского текста то вы не заметите разницы если откроете его и в кодировке UTF-8 и UTF-16 и ASCII (прим. в UTF-16 такие символы все равно будут закодированы двумя байтами, по этому вы не увидите разницы, если ваш редактор будет игнорировать нулевые байты), и так до момента пока вы не начнете работать с национальным алфавитом.
Сравним на практике как будет выглядеть фраза «Hello мир» в трех разных кодировках: Windows-1251 (русская кодировка), ISO-8859-1 (кодировка западно-европейских языков), UTF-8 (юникод-кодировка). Суть данного примера состоит в том что фраза написана на двух языках. Посмотрим как она будет выглядеть в разных кодировках.
В кодировке ISO-8859-1 нет таких символов «м», «и» и «р».
Теперь давайте поработаем с кодировками и разберемся как преобразовать строку из одной кодировки в другую и что будет если преобразование неправильное, или его нельзя осуществить из за разницы в кодировках.
Будем считать что изначально фраза была записана в кодировке Windows-1251. Исходя из таблицы выше запишем эту фразу в двоичном виде, в кодировке Windows-1251. Для этого нам потребуется всего только перевести из десятеричной или шестнадцатиричной системы (из таблицы выше) символы в двоичную.
01001000 01100101 01101100 01101100 01101111 00100000 11101100 11101000 11110000
Отлично, вот это и есть фраза «Hello мир» в кодировке Windows-1251.Теперь представим что вы имеете файл с текстом, но не знаете в какой кодировке этот текст. Вы предполагаете что он в кодировке ISO-8859-1 и открываете его в своем редакторе в этой кодировке. Как сказано выше с частью символов все в порядке, они есть в этой кодировке, и даже находятся на тех же местах, но вот с символами из слова «мир» все сложнее. Этих символов в этой кодировке нет, а на их местах в кодировке ISO-8859-1 находятся совершенно другие символы. А конкретно «м» — позиция 236, «и» — 232. «р» — 240. И на этих позициях в кодировке ISO-8859-1 находятся следующие символы позиция 236 — символ "ì", 232 — "è", 240 — "ð"
Значит фраза «Hello мир» закодированная в Windows-1251 и открытая в кодировке ISO-8859-1 будет выглядеть так: «Hello ìèð». Вот и получается что эти две кодировки совместимы лишь частично, и корректно перекодировать строку из одной кодировке в другую не получится, потому что там просто напросто нет таких символов.
Тут и будут необходимы юникод-кодировки, а конкретно в данном случае рассмотрим UTF-8. То что символы в ней могут быть закодированы разным количеством байтов от 1 до 4 мы уже выяснили. Теперь стоит сказать что с помощью UTF могут быть закодированы не только 256 символов, как в двух предыдущих, а вобще все символы юникода
Работает она следующим образом. Первый бит каждого байта кодирующего символ отвечает не за сам символ, а за определение байта. То есть например если ведущий (первый) бит нулевой, то это значит что для кодирования символа используется всего один байт. Что и обеспечивает совместимость с ASCII. Если внимательно посмотрите на таблицу символов ASCII то увидите что первые 128 символов (английский алфавит, управляющие символы и знаки препинания) если их привести к двоичному виду, все начинаются с нулевого бита (будьте внимательны, если будете переводить символы в двоичную систему с помощью например онлайн конвертера, то первый нулевой ведущий бит может быть отброшен, что может сбить с толку).
01001000 — первый бит ноль, значит 1 байт кодирует 1 символ -> «H»
01100101 — первый бит ноль, значит 1 байт кодирует 1 символ -> «e»
Если первый бит не нулевой то символ кодируется несколькими байтами.
Для двухбайтовых символов первые три бита должны быть такие — 110
110 10000 10 111100 — в начале 110, значит 2 байта кодируют 1 символ. Второй байт в таком случае всегда начинается с 10. Итого отбрасываем управляющие биты (начальные, которые выделены красным и зеленым) и берем все оставшиеся ( 10000111100 ), переводим их в шестнадцатиричный вид (043С) -> U+043C в юникоде равно символ «м».
для трех-байтовых символов в первом байте ведущие биты — 1110
1110 1000 10 000111 10 1010101 — суммируем все кроме управляющих битов и получаем что в 16-ричной равно 103В5, U+103D5 — древнеперситдская цифра сто ( 10000001111010101 )
для четырех-байтовых символов в первом байте ведущие биты — 11110
11110 100 10 001111 10 111111 10 111111 — U+10FFFF это последний допустимый символ в таблице юникода ( 100001111111111111111 )
Теперь, при желании, можем записать нашу фразу в кодировке UTF-8.
UTF-16
UTF-16 также является кодировкой переменной длинны. Главное ее отличие от UTF-8 состоит в том что структурной единицей в ней является не один а два байта. То есть в кодировке UTF-16 любой символ юникода может быть закодирован либо двумя, либо четырьмя байтами. Давайте для понятности в дальнейшем пару таких байтов я буду называть кодовой парой. Исходя из этого любой символ юникода в кодировке UTF-16 может быть закодирован либо одной кодовой парой, либо двумя.
Начнем с символов которые кодируются одной кодовой парой. Легко посчитать что таких символов может быть 65 535 (2в16), что полностью совпадает с базовым блоком юникода. Все символы находящиеся в этом блоке юникода в кодировке UTF-16 будут закодированы одной кодовой парой (двумя байтами), тут все просто.
символ «o» (латиница) — 00000000 01101111
символ «M» (кириллица) — 00000100 00011100Теперь рассмотрим символы за пределами базового юникод диапазона. Для их кодирования потребуется уже две кодовые пары (4 байта). И механизм их кодирования немного сложнее, давайте по порядку.
Для начала введем понятия суррогатной пары. Суррогатная пара — это две кодовые пары используемые для кодирования одного символа (итого 4 байта). Для таких суррогатных пар в таблице юникода отведен специальный диапазон от D800 до DFFF. Это значит, что при преобразовании кодовой пары из байтового вида в шестнадцатиричный вы получаете число из этого диапазона, то перед вами не самостоятельный символ, а суррогатная пара.
Чтобы закодировать символ из диапазона 10000 — 10FFFF (то есть символ для которого нужно использовать более одной кодовой пары) нужно:
- из кода символа вычесть 10000(шестнадцатиричное) (это наименьшее число из диапазона 10000 — 10FFFF)
- в результате первого пункта будет получено число не больше FFFFF, занимающее до 20 бит
- ведущие 10 бит из полученного числа суммируются с D800 (начало диапазона суррогатных пар в юникоде)
- следующие 10 бит суммируются с DC00 (тоже число из диапазона суррогатных пар)
- после этого получатся 2 суррогатные пары по 16 бит, первые 6 бит в каждой такой паре отвечают за определение того что это суррогат,
- десятый бит в каждом суррогате отвечает за его порядок если это 1 то это первый суррогат, если 0, то второй
Для примера зашифруем символ, а потом расшифруем. Возьмем древнеперсидскую цифру сто (U+103D5):
- 103D5 — 10000 = 3D5
- 3D5 = 0000000000 1111010101 (ведущие 10 бит получились нулевые приведем это к шестнадцатиричному числу, получим 0 (первые десять), 3D5 (вторые десять))
- 0 + D800 = D800 ( 110110 0 000000000 ) первые 6 бит определяют что число из диапазона суррогатных пар десятый бит (справа) нулевой, значит это первый суррогат
- 3D5 + DC00 = DFD5 ( 110111 1 111010101 ) первые 6 бит определяют что число из диапазона суррогатных пар десятый бит (справа) единица, значит это второй суррогат
- итого данный символ в UTF-16 — 1101100000000000 1101111111010101
- переведем в шестнадцатиричный вид = D822DE88 (оба значения из диапазона суррогатных пар, значит перед нами суррогатная пара)
- 110110 0 000100010 — десятый бит (справа) нулевой, значит первый суррогат
- 110111 1 010001000 — десятый бит (справа) единица, значит второй суррогат
- отбрасываем по 6 бит отвечающих за определение суррогата, получим 0000100010 1010001000 (8A88)
- прибавляем 10000 (меньшее число суррогатного диапазона) 8A88 + 10000 = 18A88
- смотрим в таблице юникода символ U+18A88 = Tangut Component-649. Компоненты тангутского письма.
Вот некоторые интересные ссылки по данной теме:
habr.com/ru/post/158895 — полезные общие сведения по кодировкам
habr.com/ru/post/312642 — про юникод
unicode-table.com/ru — сама таблица юникод символов
- Введение в Unicode