Как определить кодировку файла python

Обновлено: 05.07.2024

Это еще один блог, который ориентирован для ведения по тематике языка программирования Python. Никаких репостов тут не будет, исключительно информация из под моих рук. Надеюсь, что информация на этом блоге, будет полезна хотя бы начинающим.

понедельник, 7 декабря 2015 г.

Кодировки, юникод; Работа с кодировками и юникодом в Python

Кодировки (общее)

Я рекомендую вдумчиво изучить материалы по данным ссылкам, Т.К. Всех начинающих программистов, работа с кодировками, нередко вводит в ступор.

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

Для начала, нам необходимо помнить то, что компьютер не умеет хранить в себе буквы, он умеет хранить двоичный код, который состоит из 0 и 1. При помощи нулей и единиц, наши тексты зашифрованы в памяти компьютера. Вот тут и вступает в дело то, что мы называем кодировкой.

Допустим, что кодировка cp866, содержит в себе соответствие кодам "0101" и "0011", для букв "у" и "х". Если мы попробуем прочесть слово записанное двоичным кодом вида "0011 0101 0011 0101" при помощи кодировки cp866, мы получим слово "хуху".

Немножко проясняется? То, что мы назваем кодировкой, всего лишь таблица, где хранятся условные двоичные коды и символы(буквы), которые соответствуют этим кодам в конкретной кодировке. Историю о том, почему так получилось, читаем все по тем же ссылкам, которые я давал выше.

Какой мы можем сделать общий вывод? Если у нас будет текст, который будет содержать себе буквы в двоичном коде и будет предназначен для прочтения кодировкой cp1251, а мы попробуем прочесть его при помощи другой кодировки, мы получим совершенно нечитаемый и неправильный набор букв. Также помним про то, что далеко не все кодировки содержат в себе русские буквы. Кпримеру кодировка ascii русских букв не имеет вовсе, да что там, очень мало кодировок, которые содержат в себе русские символы. И при попытке прочесть текст, который закодирован cp1251 при помощи какой-то индусской кодировки, мы получим айсуконфлексы, юсуконфлексы и другие нечитаемые псевдографические умляуты.

Как быть? Если вы удосужились прочесть что-то по ссылкам выше, вы должны были прочесть про такое явление, как юникод(unicode). Это символьное отображение всех символов, которые существуют в письменности на земле. Кпримеру буква "а", будет записана, как "\u0430", а буква "Б", как "\u0431". Каждый символ имеет в юникоде свое обозначение. Акцентирую ваше внимание, что юникод не кодировка - это способ символьного отображения других символов, где каждый символ имеет строго свое индивидуальное обозначение. Из этого следует вывод, что при необходимости мы можем воспользоваться таблицей юникода и закодировать соответствующие символы(буквы) в двоичный код, который будет соответствовать выбранной кодировке. Также мы можем раскодировать символы с любой кодировки в юникод. Приметивный пример этого может выглядеть примерно так:

Раскодируем букву "а" с cp1251 в юникод и закодируем в cp866.

(0101 -> \u0430 -> 1001)


Нам ненужно помнить какие-то соответствия, за нас это помнят таблицы, нам нужно всего лишь помнить, что существуют разные кодировки и текст закодированный для одной кодировки, нельзя читать при помощи другой.

Мы можем раскодировать текст с какой-то кодировки в универсальный юникод, где каждый символ будет иметь индивидуальное обозначение.

Мы можем закодировать текст из юникода в необходимую нам кодировку.


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

Последнее, что я скажу про кодировки вобщем, что мы можем наблюдать кодировку utf-8 с bom и без bom. В некоторых системах, чтение байтов с двоичным кодом, которые отвечают за какие-то символы в кодировках, может производиться слева на право, а в каких-то справа на лево. Наша буква "а", записанная двоичнім кодом "0101", может читаться слева на право "0101", также и справа на лево "1010", что дает нам несколько разный набор нулей и единиц. Если мы запишем эту букву в системе, которая читает этот код слева на право, а попытаемся прочесть в системе, которая читает этот код справа на лево, мы получим курказябры, умляуты, вопросительные знаки и Т.Д. Символ bom - это первый символ в текстовом пространстве, который сообщает, в каком порядке необходимо читать наш код, что спасает нас от разночтения в разных операционных системах. Для начала, я советую использовать utf-8 без bom, что избавит нас от необходимости учитывать первый символ bom. У нас ничего страшного, без символа bom, не произойдет.

Юникод и кодировки в Python

Для начала учтем то, что я буду выдавать дальнейшую информацию исходя из того, что все это происходит под OS Windows, но постараюсь давать необходимые комментарии в местах, где это будет актуально для других OS.

Нам необходимо запомнить, что все, что происходит в консоли, происходит в кодировке cp866. Это актуально для Windows. При использовании Linux и MacOS, консоль будет в кодировке utf-8. Определенные шаги, которые мы будем совершать с кодировками в Python, будут актуальны для Windows и неактуальны для других систем, и наоборот.


При создании файла, в котором мы будем писать код нашей программы, первой строкой мы вписываем следующее:

Это сообщит Python, что содержимое файла, хранится в кодировке utf-8. Никогда не пренебрегайте этой строкой, она очень важна!


Дальнейшие пляски вокруг создания юникодовых литералов и кодирования/декодирования ввода и вывода данных в консоли, будут актуальны для OS Windows. Консоль MacOs и Linux используют кодировку utf-8, что позволяет работать с русским текстом без кодирования/декодирования строк. Правда, что в некоторых случаях при разнообразных настройках OS Linux (Чаще) и в MacOs (реже), мы можем получать неожиданные результаты работы с кодировками. В случаях совсем уж непонятных результатов, мы методом тыка пытаемся определить, что же не так, а еще лучше гуглим на тему кодировок в необходимой OS, а также кодировок в определенной OS при программировании на Python.


Для начала, мы поговорим о юникоде в Python. Текст в юникоде имеет свой тип данных "unicode". Создать юникодовый литерал(строку), можно несколькими способами.

Во-первых, мы можем указать модификатор "u" перед кавычками литерала "" или '':

Если мы не укажем модификатор "u", мы получим строчный литерал типа str в кодировке консоли: Для Windows - cp866, для остальных OS, с большой долей вероятности, utf-8.

Как видим, при попытке вывести русскую букву без использования оператора print, оканчивается выводом некой символьной записи этой буквы, но если мы воспользуемся оператором print, все будет нормально:

Нам необходимо запомнить, что оператор print, автоматически пытается вывести нам текст, подразумевая кодировку консоли - cp866 для Windows и utf-8 для других OS. Также, она автоматически кодирует unicode в кодировку консоли, что позволяет правильно отображать юникодовые литералы. И естественно, если нам попадется текст в кодировке cp1251, а мы попытаемся вывести его оператором print, мы получим курказябры, Т.К. print попытается вывести текст, ориентируясь на то, что он записан в кодировке консоли, что приводит нас к тому, что текст должен быть либо в кодировке, соответствующей кодировке консоли, либо в юникоде, либо мы должны явно раскодировать неподходящую для вывода кодировку в юникод.


В теории, если мы создадим юникодовый литерал, через print, мы должны получить нормальный читаемый текст, но как показала практика, в каких-то случаях юникодовый литерал, даже при использовании оператора print, вылазит курказябрами. Опять таки, в теории все, у кого консоль находится в utf-8, могут вовсе не забивать себе голову использованием всего богатства кодирований/декодирований для ввода и вывода данных в консоли. Для теста, как оно работать будет, выполните следующее (Актуально для MacOs и Linux):

В теории должно выйти на экран читаемым текстом.

По хорошему, тоже должно выйти читаемым текстом, но полевые испытания показали, что почему-то невсегда.

a = "Привет".decode('utf-8')

Посути, мы ввели текст в utf-8 и декодировали его в unicode, Т.Е. Практически повторили предыдущий эксперимент, но тут мы дали строку в utf-8 и явно декодировали ее в unicode.

a = u"Привет".encode('utf-8')

Тут мы создали юникодовый литерал и явно преобразовали его в utf-8. Скорее всего, русский текст будет выводиться хорошо и без юникодового литерала и разных там кодирований/декодирований. Но также юникодовый литерал, должен правильно выводиться при использовании оператора print.


Еще один способ создания юникодового литерала, заключается в использовании функции преобразования типов unicode(), где первым аргументом мы передаем строчный литерал, а вторым указываем его текущую кодировку. На выходе получим юникодовый литерал:

>>> a = unicode('Привет', 'cp866')

Методы кодирования/декодирования

Для объекта строки и юникода, есть существуют методы, позволяющие кодировать и декодировать данные. Метод decode() для строчного литерала, позволит декодировать его из текущей кодировки в юникод, создав тем самым объект unicode.

Метод encode(), позволяет закодировать юникодовый литерал в строчный, явно указав необходимую кодировку.

Подведем итоги

Мы неможем прочесть текст, закодированный в одной кодировке при помощи другой. Мы пытаемся все держать в юникоде, чтобы проще было ориентироваться в том, что происходит в рамках нашего кода, Т.Е. Все входящие данные, мы преобразовываем в юникодовый литерал, а при необходимости вернуть эти данные куда-то, мы явно кодируем их в необходимую кодировку. Если вы попытаетесь заставить взаимодействовать между собой данные в разных кодировках или данные закодированные в какой-то кодировке и данные в юникоде, в лучшем случае выпадет трекбек, в худшем трекбека небудет, но и работать тоже ничего небудет.

правильное обнаружение кодировки все время невозможно.

однако некоторые кодировки оптимизированы для конкретных языков и языков они не случайны. Некоторый символ последовательности всплывают все время, в то время как другие последовательности не имеют смысла. Ля человек, свободно владеющий английским языком, который открывает газета и находит " txzqJv 2!dasd0a QqdKjvz " мгновенно распознает, что это не английский (даже если это полностью состоит из английских букв). Изучая множество "типичных" текстов, компьютерный алгоритм может имитировать это вид беглости и сделать образованный угадайте язык текста.

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

вы также можете использовать UnicodeDammit. Он попробует следующее методы:

другой вариант для разработки кодировки-использовать libmagic (который является кодом command). Существует изобилие доступны привязки python.

привязки python, которые живут в дереве источника файлов, доступны как python-magic (или python3-магия) пакет Debian. Если можно определить кодировку файла, выполнив:

есть с одинаковыми именами, но несовместимо,python-magic пакет pip на pypi, который также использует libmagic. Он также может получить кодировку, выполнив:

Некоторые стратегии кодирования, пожалуйста, раскомментируйте по вкусу :

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

вот пример чтения и принятия по номиналу a chardet кодировка предсказание, чтение n_lines из файла в случае, если он большой.

chardet также дает вам вероятность (т. е. confidence ) его предсказания кодирования (не посмотрел, как они придумали это), который возвращается с его предсказанием от chardet.predict() , так что вы могли бы работать, если хотите.

Это, в принципе, невозможно определить кодировку текстового файла, в общем случае. Так что нет, нет стандартной библиотеки Python, чтобы сделать это за вас.

Если у вас есть более конкретные знания о текстовом файле (например, что это XML), могут быть библиотечные функции.

Если вы знаете некоторое содержимое файла, вы можете попытаться декодировать его с помощью нескольких кодировок и посмотреть, что отсутствует. В общем, нет никакого способа, так как текстовый файл является текстовым файлом, и это глупо;)

в зависимости от вашей платформы, я просто предпочитаю использовать Linux shell

Правильное определение кодировки все время невозможно .

Однако некоторые кодировки оптимизированы для конкретных языков, и языки не являются случайными. Некоторые последовательности символов появляются постоянно, тогда как другие последовательности не имеют смысла. Человек, свободно говорящий по-английски, который открывает газету и находит «txzqJv 2! Dasd0a QqdKjvz», сразу же поймет, что это не английский (хотя он полностью состоит из английских букв). Изучая много «типичного» текста, компьютерный алгоритм может имитировать этот вид беглости и сделать обоснованное предположение о языке текста.

Существует chardet библиотека, которая использует это исследование для определения кодировки. chardet - это порт кода автоопределения в Mozilla.

Вы также можете использовать UnicodeDammit. Он попробует следующие методы:

В принципе, невозможно определить кодировку текстового файла в общем случае. Так что нет, для этого нет стандартной библиотеки Python.

Если у вас есть более конкретные знания о текстовом файле (например, что это XML), возможно, существуют библиотечные функции.

Некоторые стратегии кодирования, пожалуйста, раскомментируйте по вкусу:

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

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

chardet также дает вам вероятность (т. Е. confidence ) его прогноза кодирования (не смотрели, как они это придумали), который возвращается с его прогнозом из chardet.predict() , так что вы может работать так или иначе, если хотите.

В зависимости от вашей платформы, я просто выберу команду linux shell file . Это работает для меня, так как я использую его в сценарии, который работает исключительно на одной из наших машин Linux.

Очевидно, что это не идеальное решение или ответ, но его можно изменить в соответствии с вашими потребностями. В моем случае мне просто нужно определить, является ли файл UTF-8 или нет.

Если вам известно какое-то содержимое файла, вы можете попытаться расшифровать его с помощью нескольких кодировок и посмотреть, что в нем отсутствует. В общем, нет никакого способа, так как текстовый файл является текстовым файлом, и это глупо;)

Этот документ (PEP 263 [1]) представляет синтаксис для декларации кодировки текста в файле исходного кода на языке Python. Информация о кодировке затем используется парсером Python для интерпретации файла на указанной кодировке. Прежде всего это улучшает интерпретацию литералов Unicode в исходном коде, и делает возможным писать в литералах Unicode с использованием например UTF-8 напрямую в редакторе, поддерживающем Unicode.

Описываемый ниже метод позволяет избежать ошибок наподобие:

Python source file encoding error

Процесс по шагам:

1. Проверьте, что текст модуля Python сохранен в кодировке UTF-8. В редакторе Notepad2 это делается через меню File -> Encoding. Этот же пункт меню позволяет перекодировать файл в кодировку UTF-8.

2. Добавьте в начало модуля строку:

После этого ошибка исчезнет.

Рекомендуемое решение. Рекомендуется сделать кодирование исходного кода Python как видимым, так и изменяемым на уровне исходного файла, применяя в каждом модуле файла исходного кода специальный комментарий в начале файла, чтобы декларировать в нем кодировку.

Чтобы настроить Python для распознавания этой декларации кодировки, необходимо ознакомиться о принципах обработки данных исходного кода Python.

[Определение кодировки]

По умолчанию Python подразумевает, что в файле принят стандарт кодирования ASCII, если не дано никаких других подсказывающих указаний. Чтобы определить кодировку исходного кода, во все исходные файлы нужно добавить "магический" комментарий в первой или второй строке исходного файла:

или (используя форматы, распознаваемые популярными редакторами):

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

Первая группа этого выражения интерпретируется как имя кодировки. Если эта кодировка не известна для Python, то во время попытки компиляции произойдет ошибка. Не должно быть никакого любого оператора Python в строке, в этой строке, где содержится декларация о кодировке. Если на первую строку регулярное выражение сработает, то вторая строка на предмет поиска кодировки игнорируется.

Чтобы обработать такие платформы, как Windows, которые добавляют маркеры Unicode BOM в начало файла Unicode, UTF-8 сигнатура \xef\xbb\xbf будет также интерпретироваться как кодировка 'utf-8' (даже если в файл не добавлен описанный магический комментарий).

Если исходный файл использует одновременно сигнатуру маркера UTF-8 BOM, и магический комментарий, то разрешенной кодировкой для комментария будет только 'utf-8'. Любая другая кодировка в этом случае приведет к ошибке.

[Примеры]

Ниже приведено несколько примеров, показывающих разные стили определения кодировки исходного кода в начале файла Python.

1. Двоичный интерпретатор и использование файла стиля Emacs:

2. Без строки интерпретатора, используя чистый текст:

3. Текстовые редакторы могут иметь разные способы определения кодировки файла, например:

4. Без комментария кодировки парсер Python подразумевает, что это текст ASCII:

[Плохие примеры]

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

A. Пропущенный префикс "coding:":

B. Комментарий кодировки не находится на строке 1 или 2:

C. Не поддерживаемая кодировка:

PEP [1] основывается на следующих концепциях, которые должны быть реализованы, чтобы включить использование такого "магического" комментария:

1. Во всем исходном файле Python должна использоваться одинаковая кодировка. Встраивание по-другому закодированных данных приведет к ошибке декодирования на этапе компиляции исходного файла кода Python.

Можно использовать в исходном коде любое кодирование, которое позволяет обработать две первые строки способом, показанным выше, включая ASCII-совместимое кодирование, а также определенные многобайтовые кодировки, такие как Shift_JIS. Это не включает кодировки наподобие UTF-16, которые используют два или большее количество байтов для всех символов. Причина в том, что требуется сохранять простым алгоритм декодирования кодировки в парсере ключевых слов (токенизатор).

3. Комбинация токенизатор/компилятор Python должна быть обновлена, чтобы работать следующим образом:

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

Для обратной совместимости с существующим кодом, который в нестоящее время использует не-ASCII кодировку в строковых литералах без декларации кодирования, реализация будет представлена двумя фразами:

1. Разрешается использование не-ASCII кодировки в строковых литералах и комментариях, при этом внутренне будет рассматриваться отсутствие объявление кодировки как декларация "iso-8859-1". В результате произвольная строка байт будет корректно обработана с предоставлением совместимости с Python 2.2 для литералов Unicode, которые содержат байты, не попадающие в кодировку ASCII.

Будут выдаваться предупреждения по мере появления non-ASCII байтов на входе, один раз на неправильно закодированный входной файл.

2. Удаление предупреждения, и изменение кодировки по умолчанию на "ascii".

Если строка Unicode с декларацией кодирования будет передана в compile(), будет сгенерировано событие SyntaxError.

Кодировку unicode можно считать рабочей кодировкой питона т.к. она предназначена для её использования в самом скрипте - для разных операций над строками.
Внешняя кодировка (объект str) предназначена для хранения и передачи текстовой информации вне скрипта, например для сохранения в файл или передачи по сети. Поэтому в данной статье я её назвал внешней. Самой используемой кодировкой в мире является utf8 и число приложений переходящих на эту кодировку растет каждый день, таким образом превращаясь в "стандарт". Эта кодировка хороша тем что для хранения текста она занимает оптимальное кол-во памяти и с помощью её можно закодировать почти все языки мира ( в отличие от cp1251 и подобных однобайтовых кодировок). Поэтому рекомендуется везде использовать utf8, и при написании скриптов.

Использование

Скрипт питона, в самом начале скрипта указываем кодировку файла и сохраняем в ней файл

для того что-бы интерпретатор python понял в какой кодировке файл


Строки в скрипте
Строки в скрипте хранятся байтами, от кавычки до кавычки:

= 6 байт при cp1251
= 12 байт при utf8

Если перед строкой добавить символ u, то при запуске скрипта, эта байтовая строка будет декодирована в unicode из кодировки указанной в начале:

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


Загрузка и сохранение файла

Текст в скрипте

Процедуре print текст желательно передавать в рабочей кодировке либо кодировать в кодировку ОС.
Результат скрипта при запуске из консоли windows XP:

В последней строке print преобразовал unicode в cp866 автоматический, см. следующий пункт


Авто-преобразование кодировки
В некоторых случаях для упрощения разработки python делает преобразование кодировки, пример с методом print можно посмотреть в предыдущем пункте.
В примере ниже, python сам переводит utf8 в unicode - приводит к одной кодировке для того что-бы сложить строки.

Как видим результирующая строка "c" в unicode. Если бы кодировки строк совпадали то авто-перекодирования не произошло бы и результирующая строка содержала кодировку слагаемых строк.
Авто-перекодирование обычно срабатывает когда происходит взаимодействие разных кодировок.


Пример авто-преобразования кодировок в сравнении

В сравнении 1, кодировка utf8 преобразовалась в unicode и сравнение произошло корректно.
В сравнении 2, сравниваются кодировки одного вида - обе внешние, т.к. кодированы они в разных кодировках условие выдало что они не равны.
В сравнении 3, выпало предупреждение из за того что выполняется сравнение кодировок разного вида - рабочая и внешняя, а авто-декодирование не произошло т.к. стандартная внешняя кодировка = utf8, и декодировать строку в кодировке cp1251 методом utf8 питон не смог.

При выводе списка, происходит вызов [<repr>]() который возвращает внутреннее представление этого спиcка - print 1 и 2 являются аналогичными. Для корректного вывода списка, его нужно преобразовать в строку - print 3.

Читайте также: