Про кодировки: Должна остаться только одна!
Sep 19, 2013 17:44 · 460 words · 3 minute read
Сегодня пришлось мне повозиться с кодировкой строк. Задача: записать в базу русские буквы. Реализация: Python (MySQLdb) + MySQL + OS X. Не зря я здесь указал платформу ;) Казалось бы, тривиальная задача, ан нет - в таблицу падали крякозябры.
Сначала я грешил на питон с его странными строками (Python 2.7). Известно, что обычная строка там - просто последовательность байтов. Получить юникодную строку можно несколькими способами:
- указать в начале файла
# -*- coding: UTF-8 -*-
, т.о. весь файл будет читаться как UTF-8; - явно указать, что строка юникодная:
u'я юникод'
; - сконвертировать в нужную кодировку:
"Текст".decode('utf-8')
.
На последнем пункте стоит остановиться подробнее. Конвертацию куда мы указали, а откуда - нет. На самом-то деле берётся текущая кодировка сессии. Для Windows это CP866 или Windows-1251 (русская локализация), для linux latin-1 или UTF-8 (всё чаще), для OS X - latin-1 (aka Windows-1252). Таким образом, decode переводит строку из кодировки консоли в то, что указано в первом параметре. Есть ещё и второй параметр - что делать с неизвестными символами, но тут уже лучше сходить к документации. Есть ещё метод encode - он делает обратное преобразование: из кодировки в параметре в последовательность байт. В общем, запутаться очень просто :) На помощь может прийти библиотека chardet - она позволяет определить кодировку строки (с определённой вероятностью):
>>> a = 'sdfds'
>>> import chardet
>>> print chardet.detect(a)
{'confidence': 1.0, 'encoding': 'ascii'}
>>> a = 'авыаыв'
>>> print chardet.detect(a)
{'confidence': 0.99, 'encoding': 'utf-8'}
Ужас, да? :) Спешу обрадовать: в Python 3.x такого безобразия нет, там все строки в UTF-8.
Но вернёмся к нашей проблеме: скрипт в utf, кодировка строк не указана => кодировка скрипта => utf, база в utf, таблица в utf, даже поле в utf. Так почему же пишется мусор?! Оказалось всё до банальности просто - не была указана кодировка для соединения с базой, а она по умолчанию берётся не из настроек MySQL (где указана как utf_general_ci), а из сессии! И тут вспоминаем, что мы работаем под OS X, а там по умолчанию она равна latin-1. Проверяем в декодере Лебедева: действительно, строка в Windows-1252 записана как UTF-8.
Вроде проблему закрыли, какие выводы? Во-первых, везде используйте utf-8 (если, конечно, объём не критичен); во-вторых, везде указывайте явно нужную вам кодировку - не полагайтесь на значение по умолчанию; в-третьих, строка в Python 2.x - последовательность байт (decode во что перегнать, encode - откуда). Вот, вроде бы и всё :) Выпьем же за разработчиков UTF-8 и победу этой чудесной кодировки!
P.S. Полезные ссылки:
- Юникод для чайников. Там же ответ почему PHP не поддерживает юникод из коробки.
- Про работу с MySQL и DBF из Python.
- Про кодировку в консоли.
P.P.S. На самом деле не такая уж UTF и замечательная кодировка. Понять и запомнить все нюансы, по-моему, просто невозможно… Но всё это можно скрыть за слоями абстракций, а свою главную задачу - унификацию - она всё-таки решает.