Как определить кодировку последовательности байтов в Python
30 октября 2017 г.
Archy
Просмотров: 30382
RSS
3
Python для начинающих
ascii, chardet, chardetect, utf-8, байты в Pyhon

Как узнать, в какой кодировке записана последовательность байтов? Коротки ответ: никак. Кто-то должен вам сообщить.
В некоторых коммуникационных протоколах и файловых форматах, например HTTP и XML, предусмотрены заголовки, в которых явно указывается, как закодировано содержимое.
Можно быть уверенным, что поток байтов представлен не в кодировке ASCII, если он содержит значения, больше 127, а сам способ построения UTF-8 и UTF-16 исключает определенные последовательности байтов.
Но и с учетом всего этого никогда нет стопроцентной уверенности в том, что некий двоичный файл записан в кодировке ASCII или UTF-8 просто потому, что в нем не встречаются определенные комбинации битов.
Если вы являетесь веб-разработчиком и часто имеете дело с WordPress то наверное не один раз сталкивались с кодировкой UTF-8. Например, любая тема, themeforest WoodMart не исключение, работает полностью с UTF-8.
Однако известно, что в естественных языках есть свои правила и ограничения. Поэтому если допустить, что поток байтов — это простой текст на естественном языке, то его кодировку можно попытаться определить с помощью различных эвристических правил и статистики.
Например, если часто встречается байт b’\x00′, то это, скорее всего, 16- или 32-разрядная кодировка, но не 8-разрядная схема, потому что нулевые байты в открытом тексте — очевидная ошибка.
Если часто встречается последовательность b’\x20\x00′, то это, наверное, символ пробела(U+0020) в кодировке UTF-16LE, а не малоизвестный символ U+2000 EN QUAD.
Именно так и работает пакет Chardet — универсальный детектор кодировки символов( https://pypi.python.org/pypi/chardet ) — который пытается распознать одну из 30 поддерживаемых кодировок.
Chardet — написанная на Python библиотека, которую вы можете включить в свою программу, а, кроме нее, пакет содержит также командную утилиту chardetect. Вот обычный пример запуска команды chardetect если дать ей имя файла как первый аргумент:
Как же определить кодировку последовательности байтов файла не из командной строки, а использовав модуль chardet?
Используйте следующий пример кода:
Хотя в самих двоичных последовательностях закодированного текста обычно нет явных указаний на кодировку, в некоторых UTF-форматах в начале файла может находиться маркер порядка байтов.
Модуль chardet в Python, определение кодировки
Когда мы думаем о тексте, то представляем слова и буквы, которые видим на экране компьютера. Но компьютеры не работают с буквами и символами. Они имеют дело с битами и байтами. Каждый фрагмент текста, который выводится на экране, на самом деле хранится в определенной кодировке символов. Существует множество различных кодировок, некоторые из которых оптимизированы для определенных языков, таких как русский, китайский или английский, а другие могут использоваться для нескольких языков. Грубо говоря, кодировка символов обеспечивает соответствие между тем, что мы видим на экране, и тем, что компьютер фактически хранит в памяти и на диске.
Модуль chardet , это автоматический детектор кодировки текста и является портом кода автоопределения в Mozilla. Этот модуль поможет определить кодировку символов, если вдруг на экране появятся "кракозябры".
Модуль chardet отлично поддерживает и определяет русские кодировки: KOI8-R, MacCyrillic, IBM855, IBM866, ISO-8859-5, windows-1251(Cyrillic)
Установка модуля chardet в виртуальное окружение.
Примеры автоматического определения кодировки символов:
Самый простой способ автоматически определить кодировку — это использовать функцию обнаружения detect() модуля chardet .
Расширенное использование модуля chardet .
Если имеется большой объем текста/данных, то можно вызывать обнаружение кодировки постепенно. Как только модуль будет достаточно уверен в своих результатах, он остановится.
Для такого поведения необходимо создать объект UniversalDetector(), затем повторно вызывать его метод подачи .feed() с каждым блоком текста. Если созданный детектор достигнет минимального порога достоверности, он установит для Detector.done значение True.
В конце работы детектора необходимо вызвать Detector.close() , который выполнит некоторые окончательные вычисления в случае, если детектор не достиг минимального порога достоверности.
Пример определения кодировки нескольких файлов.
Для определения кодировки текстовых файлов, их необходимо открывать в режиме чтения байтов: more='rb'
Character Encodings and Detection with Python, chardet, and cchardet
If your name is José, you are in good company. José is a very common name. Yet, when dealing with text files, sometimes José will appear as José, or other mangled array of symbols and letters. Or, in some cases, Python will fail to convert the file to text at all, complaining with a UnicodeDecodeError .
Unless only dealing with numerical data, any data jockey or software developer needs to face the problem of encoding and decoding characters.
Why encodings?
Ever heard or asked the question, «why do we need character encodings?» Indeed, character encodings cause heaps of confusion for software developer and end user alike.
But ponder for a moment, and we all have to admit that the «do we need character encoding?» question is nonsensical. If you are dealing with text and computers, then there has to be encoding. The letter «a», for instance, must be recorded and processed like everything else: as a byte (or multiple bytes). Most likely (but not necessarily), your text editor or terminal will encode «a» as the number 97. Without the encoding, you aren’t dealing with text and strings. Just bytes.
Encoding and decoding
Think of character encoding like a top secret substitution cipher, in which every letter has a corresponding number when encoded. No one will ever figure it out!
| a: 61 | g: 67 | m: 6d | s: 73 | y: 79 |
| b: 62 | h: 68 | n: 6e | t: 74 | z: 7a |
| c: 63 | i: 69 | o: 6f | u: 75 | |
| d: 64 | j: 6a | p: 70 | v: 76 | |
| e: 65 | k: 6b | q: 71 | w: 77 | |
| f: 66 | l: 6c | r: 72 | x: 78 |
Let’s do the encoding with a table like the above and write everything as numbers:
Exit fullscreen mode
The above 4 character codes are hexadecimal: 73, 70, 61, 6d (the escape code \x is Python’s way of designating a hexadecimal literal character code). In decimal, that’s 115, 112, 97, and 109. Try the above print statement in a Python console or script and you should see our beloved «spam». It was automatically decoded in the Python console, printing the corresponding letters (characters).
But let’s be more explicit, creating a byte string of the above numbers, and specifying the ASCII encoding:
Exit fullscreen mode
Again, «spam» . A canned response, if I ever heard one.
We are encoding and decoding! There you have it.
The complex and beautiful world beyond ASCII
What happens, however, with our dear friend José? In other words, what is the number corresponding to the letter «é»? Depends on the encoding. Let’s try number 233 (hexadecimal e9), as somebody told us that might work:
Exit fullscreen mode
That didn’t go over well. The error complains that 233 is not in the 0-127 range that ASCII uses.
No problem. We heard of this thing called Unicode, specifically UTF-8. One encoding to rule them all! We can just use that:
Exit fullscreen mode
Still, no dice! After much experimentation, we find the ISO-8859-1 encoding. This is a Latin (i.e. European-derived) character set, but it works in this case, as the letters in «José» are all Latin.
Exit fullscreen mode
So nice to have our friend back in one piece.
ISO-8859-1 works if all you speak is Latin.

That is not José. It is a picture of another friend, who speaks Latin.
UTF-8 is our friend
Once upon a time, everyone spoke «American» and character encoding was a simple translation of 127 characters to codes and back again (the ASCII character encoding, a subset of which is demonstrated above). The problem is, of course, that if this situation ever did exist, it was the result of a then U.S. dominated computer industry, or simple short-sightedness, to put it kindly (ethnocentrist and complacent may be more descriptive and accurate, if less gracious). Reality is much more complex. And, thankfully, the world is full of a wide range of people and languages.
Good thing that Unicode has happened, and there are character encodings that can represent a wide range of the characters used around the world. You can see non-Ascii names such as «Miloš» and «María», as well as 张伟. One of these encodings, UTF-8, is common. It is used on this web page, and is the default encoding since Python version 3.
With UTF-8, a character may be encoded as a 1, 2, 3, or 4-byte number. This covers a wealth of characters, including ♲, 水, Ж, and even . UTF-8, being variable width, is even backwards compatible with ASCII. In other words, «a» is still encoded to a one-byte number 97.
Character encoding detection
While ubiquitous, UTF-8 is not the only character encoding. As José so clearly discovered above.
For instance, dear Microsoft Excel often saves CSV files in a Latin encoding (unless you have a newer version and explicitly select UTF-8 CSV).
How do we know what to use?
The easiest way is to have someone decide, and communicate clearly. If you are the one doing the encoding, select an appropriate version of Unicode, UTF-8 if you can. Then always decode with UTF-8. This is usually the default in Python since version 3. If you are saving a CSV file from Microsoft Excel, know that the «CSV UTF-8» format uses the character encoding «utf-8-sig» (a beginning-of-message, or BOM, character is used to designate UTF-8 at the start of the file). If using the more traditional and painful Microsoft Excel CSV format, the character encoding is likely «cp1252» which is a Latin encoding.
But what happens if the answer is «I don’t know»? Or, more commonly, «we don’t use character encoding» (). Or even «probably Unicode?»
These all should be interpreted as «I don’t know.»
chardet, the popular Python character detection library
If you do not know what the character encoding is for a file you need to handle in Python, then try chardet.
Exit fullscreen mode
Use something like the above to install it in your Python virtual environment.
Character detection with chardet works something like this:
Exit fullscreen mode
That may have worked for you, especially if the name variable contains a lot of text with many non-ASCII characters. In this case, it works on my machine with just «José» but it cannot be very confident, and chardet might get it wrong in other similar situations. Summary: give it plenty of data, if you can. Even b’Jos\xe9 Gonz\xe1lez’ will result in more accuracy.
Did you see in response to print(detection) , that there is a confidence level? That can be helpful.
Two ways to use character detection
There are two ways I might use the chardet library.
First, I could use chardet.detect() in a one-off fashion on a text file, to determine the first time what the character encoding will be on subsequent engagements. Let’s say there is a source system that always exports a CSV file with the same character encoding. When I contact the ever-helpful support line, they kindly inform me that they have no clue what character encoding even is, so I know I am left to my own devices. Good thing the device I have is chardet. I use it on a large source file, and determine that the encoding is cp1252 (no big surprise) and then I write my code to always with open(«filename.csv», encoding=»cp1252″) as filehandle: and go on my merry way. I don’t need character detection anymore.
The second scenario is more complex. What if I am creating a tool to handle arbitrary text files, and I will never know in advance what the character encoding is? In these cases, I will always want to import chardet and then use chardet.detect() . I may want to throw an error or warning, though, if the confidence level is below a certain threshold. If confident, I will use the suggested encoding when opening and reading the file.
cchardet, the crazy-fast Python character detection library
In the second scenario above, I may appreciate a performance boost, especially if it is an operation that is repeated frequently.
How to detect encoding of CSV file in python
In my line of work, I have to deal with a lot of spreadsheets coming my way with different type of data. I don’t control these csv files, hence I never know how they are being generated. If I were to simply read the file, I would often get something like that.
Basically, when you specify the following, you assume that the information was encoded in UTF-8 ()default) format
However, if that’s not the case and format is not UTF-8 then you get a nasty error shown previously. What to do? Try manually some common encoders, or look at the file and try to figure it out?
A much better way is to use chardet module to do it for you. Here we going to read first ten thousand bytes to figure out the encoding type. Note that chardet is not 100% accurate and you would actually see the level of confidence of encoder detection as part of chardet output. But it is still better than guessing manually.
So chardet is 73% confidence that the right encoding is «Windows-1252». Now we can use this data to specify encoding type as we trying to read the file
Related Posts:
About Al Krinker
I work as a Data Scientist at USPTO.
I am a husband and a father.
I am Brazilian Jiu Jitsu brown belt.
I enjoy coding, reading, and taking pictures of my son!