UnicodeDecodeError: can't decode byte

Александр Карпинский, Uploadcare Inc.

UnicodeDecodeError:
can't decode byte

Александр Карпинский, 2019

Актуальность темы

План на доклад

История появления Юникода

Однобайтовые кодировки

Юникод — не кодировка!

Это таблица (база данных) символов, где каждый символ обладает строго заданной функцией и набором свойств.

Что в этой таблице

https://codepoints.net/U+0430

Несколько бесполезных фактов

Таблица это хорошо, но нам по-прежнему нужно передавать информацию

Что такое кодировка

Кодировка — это способ представить строки, состоящие из символов Юникода, в виде байтов.

Актуальные кодировки: UTF-8, UTF-16 и UTF-32.

Устаревшие: UTF-7, UCS-2, UTF-24.

Unicode Transformation Formats

Количество байт, нужных для кодирования символа:

UTF-8 UTF-16 UTF-32
ASCII (00-007F) 1 2 4
000080-0007FF 2 2 4
000800-00FFFF 3 2 4
010000-10FFFF 4 4 4

Работа со строками в Python

Строки в Python

str в Python 3 или unicode в Python 2.

Строки в Python

            'Питон'[0]
            'П'
            
            
        

Строки не байты

            import base64
            base64.b64encode('Python')
            TypeError: a bytes-like object is required, not 'str'
        

Кодировки спешат на помощь

            base64.b64encode('Python'.encode('utf-8'))
            b'UHl0aG9u'
            
        

Зачем делать лишнюю работу?!

            
            
            
        

Ключ к пониманию

Все API работают только с одним типом!

str bytes
.format()
json
шаблоны
base64.b64encode()
socket
криптография

Исключения?

Что поменялось с 2.7

Очевидные известные вещи:

Что поменялось с 2.7

            import base64
            base64.b64encode(u'Python')
            'UHl0aG9u'
            
        

Портирование кода на Python 3

Делайте универсальный код

Кажется сложнее, но есть возможность сделать откат в любой момент. Не надо поддерживать две версии кода и мержить изменения.

Чаще всего достаточно адаптировать под Python 3.

Делайте универсальный код

Делайте универсальный код

Почти всегда можно обойтись без ветвлений типа if six.PY3.

Пока нашел одно исключение:
Функция type("ClassName", …, …)
в Python 2 принимает только str,
в Python 3 принимает только str.

На что ещё обратить внимание

Внутреннее представление строк в языках

В JavaScript

Строки строго в UCS-2, прописано в стандарте.

UCS-2 это почти UTF-16, но с фиксированной шириной символа.

В UCS-2 могут быть суррогатные пары.

В CPython 2

Есть версии интерпретаторов с UCS-2 и UCS-4.
UCS-2 компактнее в два раза, но есть суррогатные пары.

В CPython 3.3 и далее

Для приложения выглядит как UCS-4.

В зависимости от содержимого всей строки каждый символ может занимать 1, 2 или 4 байта.

PyPy 7.1

Внутреннее представление полностью переведено на UTF-8.

Большая победа для большинства строк.

Ничего не сломалось!

Тезисы