Это руководство покажет вам, как преобразовать строку Юникода в строку в Питоне. Если вы уже знаете о Юникоде, вы можете пропустить следующий раздел, посвященный справочной информации, и сразу же погрузиться в проблему.
Истоки Юникода
Немного о Юникоде из Википедии.
Юникод — стандарт кодирования символов, включающий в себя знаки почти всех письменных языков мира. В настоящее время стандарт является преобладающим в Интернете.
Стандарт предложен в 1991 году некоммерческой организацией «Консорциум Юникода» (англ. Unicode Consortium, Unicode Inc). Применение этого стандарта позволяет закодировать очень большое число символов из разных систем письменности: в документах, закодированных по стандарту Юникод, могут соседствовать китайские иероглифы, математические символы, буквы греческого алфавита, латиницы и кириллицы, символы музыкальной нотной нотации, при этом становится ненужным переключение кодовых страниц.
В Юникоде существует несколько форм представления( от английского Unicode transformation format, UTF): UTF-8, UTF-16(UTF-16BE, UTF-16LE) и UTF-32(UTF-32BE, UTF-32LE).В потоке данных UTF-16 младший байт может записываться либо перед старшим (англ. UTF-16 little-endian, UTF-16LE), либо после старшего (англ. UTF-16 big-endian, UTF-16BE). Аналогично существует два варианта четырехбайтовой формы представления — UTF-32LE и UTF-32BE. Все их также называют кодировками.
В Microsoft Windows NT и основанных на ней системах в основном используется форма UTF-16LE. В UNIX-подобных операционных системах GNU/Linux, BSD и Mac OS X принята форма UTF-8 для файлов и UTF-32 или UTF-8 для обработки символов в оперативной памяти.
Часто мы получаем в виде входных данных строку символов Юникода, которая не читаема обычным пользователем, но имеет ряд преимуществ перед обычным текстом, например, меньше занимает места в памяти или требует меньше времени для обработки и дальнейшей передачи. В зависимости от дальнейших требований предъявляемых к строке Юникода или в зависимости от окружения(будь то операционная система или программное обеспечение) необходимо определиться с кодировкой, которую можно и нужно применить.
Кодировка UTF-8 сейчас является доминирующей в веб-пространстве. UTF-8, по сравнению с UTF-16, наибольший выигрыш в компактности дает для текстов на латинице, поскольку латинские буквы, цифры и наиболее распространённые знаки препинания кодируются в UTF-8 лишь одним байтом, и коды этих символов соответствуют их кодам в ASCII.
UTF-16 — кодировка, позволяющая записывать символы Юникода в диапазонах U+0000…U+D7FF и U+E000…U+10FFFF (общим количеством 1 112 064). При этом каждый символ записывается одним или двумя словами (суррогатная пара).
UTF-32 — способ представления Юникода, при котором каждый символ занимает ровно 4 байта. Главное преимущество UTF-32 перед кодировками переменной длины заключается в том, что символы Юникод в ней непосредственно индексируемы, поэтому найти символ по номеру его позиции в файле можно чрезвычайно быстро, и получение любого символа n-й позиции при этом является операцией, занимающей всегда одинаковое время. Это также делает замену символов в строках UTF-32 очень простой. Напротив, кодировки с переменной длиной требуют последовательного доступа к символу n-й позиции, что может быть очень затратной по времени операцией. Главный недостаток UTF-32 — это неэффективное использование пространства, так как для хранения любого символа используется четыре байта.
Формулировка проблемы
Предположим у нас есть строка в Юникоде и нам необходимо преобразовать её в строку в Питоне.
A = 'u041fu0440u0438u0432u0435u0442'
Убедимся в типе входных данных:
>>> type(A) <class 'str'>
Метод 1. String
В Питоне 3 весь текст по умолчанию представлен строками Юникода, что также означает, что синтаксис u ‘<text>’ больше не используется.
Большинство интерпретаторов в Питоне поддерживают Юникод и при вызове функции print интерпретатор преобразует входную последовательность из unicode-escape символов в строку.
print(str(A)) # Привет
Проверять тип данных после применения метода string не имеет смысла
Метод 2. Repr()
Встроенная функция repr() возвращает строку, содержащую печатаемое формальное представление объекта.
print(repr(A)) # 'Привет'
Проверим тип данных:
print(type(repr(A))) # <class 'str'>
Метод 3. Модуль Unicodedata, функция normalize
Функция normalize() модуля Unicodedata возвращает нормальную форму для строки Юникод. Допустимые значения для формы: NFC, NFKC, NFD и NFKD.
Стандарт Юникода определяет различные формы нормализации строки на основе определения канонической эквивалентности и эквивалентности совместимости. В Юникоде несколько символов могут быть выражены по-разному. Например, символ U + 00C7 (ЛАТИНСКАЯ ЗАГЛАВНАЯ С С СЕДИЛЬЕЙ) также может быть выражен как последовательность U + 0043 (ЛАТИНСКАЯ ЗАГЛАВНАЯ С) U + 0327 (КОМБИНИРУЕМАЯ СЕДИЛЬЯ).
Для каждого символа есть две нормальные формы: нормальная форма C и нормальная форма D. Нормальная форма D (NFD) также известна как каноническое разложение и переводит каждый символ в разложенную форму. Нормальная форма C (NFC) сначала применяет каноническую декомпозицию, затем снова создает предварительно объединенные символы.
В дополнение к этим двум формам существуют две дополнительные нормальные формы, основанные на эквивалентности совместимости. В Юникоде поддерживаются определенные символы, которые обычно объединяются с другими символами. Например, U + 2160 (РИМСКАЯ ЦИФРА ОДИН) действительно то же самое, что U + 0049 (ЛАТИНСКАЯ ЗАГЛАВНАЯ БУКВА I). Тем не менее, он поддерживается в Юникоде для совместимости с существующими наборами символов, например gb2312.
Нормальная форма KD (NFKD) будет применять декомпозицию совместимости, то есть заменять все символы совместимости их эквивалентами. Нормальная форма KC (NFKC) сначала применяет декомпозицию совместимости, а затем каноническую композицию.
Даже если две строки Юникода нормализованы и выглядят одинаково для человека, если одна имеет комбинированные символы, а другая нет, они могут не совпадать.
import unicodedata print(unicodedata.normalize('NFC', A)) # Привет
Проверим тип данных после нормализации:
print(type(unicodedata.normalize('NFC', A))) # <class 'str'>
Метод 4. Список и str.join
Метод str.join() возвращает строку, которая является конкатенацией (объединением) всех элементов строк итерируемого объекта.
В итоговой строке элементы объединяются между собой при помощи строки-разделителя str.
Если в итерируемой последовательности есть какие-либо НЕ строковые значения, включая байтовые строки, то вызовется исключение TypeError.
Проверим как это работает:
print(''.join([str(i) for i in A])) # Привет
''
– пустой строковый символ соединяет с помощью метода join элементы списка, который мы составили из элементов строки A.
Поскольку мы указали обернуть каждый перебираемый элемент списка функцией str, можно смело предположить что в результате получим нужный тип данных:
print(type(''.join([str(i) for i in A]))) # <class 'str'>
Метод 5. Библиотека ftfy
Полное название этой библиотеки — Fixes text for you. Она предназначена для того, чтобы превращать плохие строки Юникода(“quotesâ€\x9d или ü) в хорошие строки Юникода(“quotes” или ü соответственно).
Давайте посмотрим как она работает в нашем примере:
import ftfy print(ftfy.fix_text(A)) # Привет
Что же она делает с типом выходных данных:
print(type(ftfy.fix_text(A))) # <class 'str'>
Отлично, то что нужно, главное чтобы библиотека оставалась доступной).
Метод 6. Модуль io
Модуль IO применим, когда необходимо выполнить операцию ввода-вывода с файлами (например, чтение или запись файлов). Можно использовать встроенные методы read() и write() для чтения или записи файла, но этот модуль дает нам гораздо больше вариантов кода для этих операций, например, запись или чтение из буфера.
В нашем простом примере это будет выглядеть так:
print(io.StringIO(A).read()) # Привет
io.StringIO работает с данными строкового типа как на входе, так и на выходе. Всякий раз, когда входная строка или поток данных состоит из байтов или символов Юникода, кодирование или декодирование данных выполняется прозрачно, и учитывается необязательный перевод зависящих от среды символов новой строки.
Метод 7. Format
Этот метод кажется наиболее мощным и эффективным, поскольку он позволяет вам работать со всеми типами данных: байтами, строками, целыми числами и числами с плавающей запятой в различных представлениях (восьмеричное, десятичное, шестнадцатеричное в разных регистрах) с использованием спецификации мини-языка, который позволяет указать не только тип данных, но и смещение, округление, заполнение символами до необходимой длины, а также позволяет работать со словарями и их индексами в различных вариациях.
Проверим на нашем примере:
print(format(A, 's')) # Привет
Здесь ‘s’ – тип форматируемого объекта – строка, используется по умолчанию. Более подробно про спецификацию и синтаксис тут.