Python encoding: все тонкости и нюансы работы с кодировками

Кодировки в Python - тема, которая часто вызывает головную боль у разработчиков. В этой статье мы разберем все тонкости и подводные камни работы с кодировками в Python: от базовых понятий до реальных кейсов и рекомендаций.

История кодировок

Давайте начнем с немного истории. Первые кодировки появились еще в 1960х годах вместе с распространением компьютеров. Одной из первых была кодировка ASCII, которая отображала латинские буквы и знаки пунктуации всего лишь в 128 символов.

Зачем вообще нужны кодировки? Дело в том, что компьютеры работают с числами. Чтобы записать текст, каждому символу нужно поставить в соответствие некое число - кодовую позицию. А таблица соответствия символов и чисел называется кодировкой.

Среди популярных кодировок можно выделить:

  • ASCII - отображает 128 символов, использует 1 байт на символ
  • UTF-8 - отображает весь набор Unicode, использует от 1 до 4 байт на символ
  • UTF-16 - отображает весь Unicode, использует 2 или 4 байта
  • Windows-1251 - для кириллицы в Windows, 1 байт на символ

Главное отличие в том, что одни кодировки могут хранить символ в 1 байте (256 значений), а другие используют 2-4 байта на символ, чтобы вместить весь набор Unicode.

Беспорядок на рабочем столе

Кодировки в Python

В Python 3 строки хранятся в формате Unicode. Это значит, что каждый символ представлен кодовой позицией в стандарте Unicode.

А в Python 2 использовалось представление в байтах. При работе с русским текстом часто использовали кодировку Windows-1251.

В Python 3 строка - это последовательность кодовых позиций Unicode, а в Python 2 - последовательность байтов в заданной кодировке.

Чтобы узнать текущую кодировку в Python, можно использовать sys.getdefaultencoding(). А чтобы сменить кодировку, применяют функцию sys.setdefaultencoding('utf-8').

Для преобразования строк из одной кодировки в другую используют методы encode() и decode():

text = 'Привет' binary = text.encode('utf-8') # Преобразует в байты utf-8 text_new = binary.decode('utf-8') # Обратно в строку 

Также для работы с кодировками в Python есть кодек (codec) - объект, который знает, как преобразовывать строку в байты и обратно. Кодек можно получить так:

import codecs encoder = codecs.getencoder('utf-8') 

При ошибках кодировки могут возникать исключения UnicodeDecodeError или UnicodeEncodeError. Чаще всего это происходит при плохом соответствии кодировок при чтении файлов или передаче данных.

Чтение и запись текстовых файлов

Одна из распространенных задач - чтение данных из текстового файла. И тут начинаются проблемы с кодировками.

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

Чтобы явно указать нужную кодировку, используют параметр encoding:

f = open('file.txt', encoding='utf-8') text = f.read() 

В Windows по умолчанию стоит cp1251, в Linux utf-8, на Mac - MacCyrillic. Поэтому лучше всегда явно указывать кодировку при работе с файлами.

Если кодировка файла заранее неизвестна, можно попробовать открыть файл с разными вариантами кодировок, пока не откроется без ошибок.

Также можно открыть файл в бинарном режиме 'rb', чтобы считать данные без преобразования байтов в строку Python.

Портрет программиста

Unicode и нормализация строк

Для решения проблем с кодировками был разработан Unicode - стандарт, который присваивает уникальный номер каждому символу.

В Unicode один символ может быть закодирован разными последовательностями байт. Например, буква "é" может записываться как один символ "é" или как два символа "e" и знак ударения.

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

В Python есть 4 формы нормализации:

  • NFC - каноническое разложение, затем композиция
  • NFD - каноническое разложение
  • NFKC - совместимое разложение и композиция
  • NFKD - совместимое разложение

Нормализацию можно выполнить с помощью функции:

import unicodedata normalized = unicodedata.normalize('NFC', text) 

Это позволяет привести строки к единому виду для сравнения, поиска, индексации данных.

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

Конвертация кодировок

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

Для конвертации строк в Python удобно использовать методы encode() и decode():

text = 'привет' binary = text.encode('utf-8') text_new = binary.decode('windows-1251') 

Главное - правильно выбрать кодировку, в которую нужно конвертировать. Лучше заранее узнать, в какой кодировке пришли исходные данные, и в какой нужен результат.

Если исходная кодировка неизвестна, можно воспользоваться библиотекой chardet, которая умеет определять кодировку текста.

Транслитерация текста

Особый случай - транслитерация, когда текст конвертируется из одного алфавита в другой. Например, из кириллицы в латиницу.

В Python есть библиотеки для транслитерации: pytranslit, transliterate, а также Unidecode.

from transliterate import translit text = translit('Привет мир!', 'ru', reversed=True) #privet mir! 

Конвертация кодировки файла

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

Например, конвертируем файл из cp1251 в utf-8:

import ftfy with open('file.txt', encoding='cp1251') as f: text = f.read() text = ftfy.fix_text(text) with open('file_new.txt', 'w', encoding='utf-8') as f: f.write(text) 

Обработка текста из неизвестных источников

Особо аккуратно нужно работать с текстом из неизвестных источников. Например, при парсинге данных из сети или из файлов от пользователей.

Здесь подстерегает много подводных камней с кодировками. Поэтому нужно использовать уже упомянутые библиотеки для определения и конвертации кодировок.

Кейс: парсинг HTML-страниц

При парсинге HTML кодировка может быть указана в заголовке страницы:

<meta charset="utf-8"> 

Если ее нет, стоит использовать библиотеки для определения кодировки, например chardet.

Кейс: работа с CSV-файлами

В CSV обычно нет информации о кодировке. Поэтому при чтении CSV лучше открывать файл в бинарном режиме, а затем декодировать.

Кейс: чтение данных из БД

В БД можно явно указать кодировку для хранения текста. Например, в MySQL по умолчанию utf8.

Главное - в скрипте на Python при получении данных из БД использовать соответствующую кодировку.

Кейс: прием данных от пользователя

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

Статья закончилась. Вопросы остались?
Комментарии 0
Подписаться
Я хочу получать
Правила публикации
Редактирование комментария возможно в течении пяти минут после его создания, либо до момента появления ответа на данный комментарий.