Главная / Блог / Кодирование строк

Кодирование строк

Кодирование строк

  • Дата: 10 сентября 2020
  • Автор: Михаил Макарик
Чтобы компьютер смог отобразить передаваемые ему символы, они должны быть представлены в конкретной кодировке. Навряд ли найдется человек, который никогда не сталкивался с кракозябрами: открываешь интернет-страницу, а там – набор непонятных знаков; хочешь прочесть книгу в текстовом редакторе, а вместо слов получаешь сплошные знаки вопроса. Причина заключается в неверной процедуре декодирования текста (если сильно упростить, то программа пытается представить американцу, например, букву «Щ», осуществляя поиск в английском алфавите).

Возникают вопросы: что происходит, кто виноват? Ответ не будет коротким.

1. Компьютер – человек

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

В этом суть кодировки. ПК запоминает любые буквы, числа и знаки в виде определенного значения из единиц и нулей. Для примера: английская буква «Y» в двоичном коде выглядит как «0b1011001», а в шестнадцатеричном как «0x59».

Для осмысленного диалога пользователя и компьютера требуется двусторонний переводчик:
– «человеческие» строки необходимо перекодировать в байты;
– «компьютерную» речь требуется преобразовать в воспринимаемые пользователем осмысленные структуры.

В языке Python за это отвечают функции encode / decode. Важно кодировать и декодировать сообщение в одинаковой кодировке, чтобы не столкнуться с проблемой бессмысленных наборов символов.

2. ASCII

Так как первые вычислительные машины были малоемкими, для представления в их памяти всего набора требуемых знаков хватало 7 бит (или 128 символов). Сюда входил весь английский алфавит в верхнем и нижнем регистрах, цифры, знаки, вспомогательные символы.
Smartiqa Encoding
Пример ASCII символов
Поначалу этого вполне хватало. Кодировка получила имя ASCII (читается как «аски» или «эски»). В Пайтоне вы и сегодня можете посмотреть на символы ASCII. Для этого имеется встроенный модуль string.
Пример – IDE

import string
 
print(string.ascii_letters)
print(string.digits)
print(string.punctuation)
 
Результат выполнения кода
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789
!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
С другими свойствами модуля можете ознакомиться самостоятельно.

Время шло, компьютеризация общества ширилась, 128 символов стало не хватать. Оставшийся последний 8-ой бит также выделили для кодирования (а это еще 128 знаков). В итоге появилось большое количество кодировок (кириллическая, немецкая и т.п.). Такая ситуация привела к проблемам. Уже в то время англичанин, получающий электронное письмо из России, мог увидеть не русские буквы, а набор непонятных закорючек.

Потребовалось указание кодировок в заголовках документов.

3. Юникод-стандарт

Как вы считаете, сколько нужно символов, чтобы хватило всем и навсегда? 10_000? Конечно, нет. Уже сегодня более 100_000 знаков имеет свое числовое представление. И это не предел. Люди постоянно придумывают новые «буквы».
Откройте свой телефон и создайте пустое сообщение. Зайдите в раздел «смайликов». Да их тут больше сотни! И это не картинки в большинстве своем. Они являются символами определенной кодировки. Если вы застали времена, когда SMS-технологии только начинали развиваться, то этих самых «смайлов» было не более десятка. Лет через 10 их количество станет «неприличным».
Упомянутая выше кодировка ASCII в своем расширенном варианте породила большое количество новых. Основная беда: имея 128 вариантов обозначить символ, мы никак не сумеем внедрить туда буквы других языков. В частности, какой-нибудь символ под номером 201 в кириллице даст совсем не русскую букву, если отослать его в Румынию. Следовательно, говоря кому-то «посмотри на 201-ый символ» мы не даем никакой гарантии, что собеседник увидит то же.

Для решения задачи был разработан стандарт Unicode. Отметим, что это не определенная кодировка, а именно набор правил. Суть юникода – связь символа и определенного числа без возможного повторения. Если мы кого-то попросим показать символ, скрытый под номером «1000», то в любой точке планеты он будет одним и тем же графическим элементом.
Smartiqa Encoding Unicode
Пример Unicode символов
Всего в Unicode стандарте на сегодня имеется место под более чем 1,1 млн знаков:

  1. UTF-8 – кодировка символов юникод в двоичном виде. Использует от 1 до 4 байт. Так как наиболее часто используемые символы занимают 1 байт (в частности, аски-символы), то UTF-8 оптимальна для английского текста, но не для азиатского.
  2. UTF-16 используется для кодирования 2-мя или 4-мя байтами. Считается удобным способом для пользователей азиатских стран с иероглифическим письмом.
  3. UTF-32 представляет все символы при помощи 4 байт. Используется редко, так как потребляет много памяти. Тем не менее, быстро работает при наличии мощностей.

4. Кодирование и декодирование строк

Пользователям Python 3 версии повезло. Все символы и документы заранее приводятся к кодировке UTF-8. И если где-то в коде вы напишете строку «Ёжик в тумане» или «אֱלִיעֶזֶר וְהַגֶזֶר», то будьте уверены, они не превратятся в абракадабру и не приведут к ошибкам.

Тем не менее, строковые методы decode / encode не потеряли свою актуальность. По умолчанию все преобразования осуществляются в кодировке UTF-8, но никто не мешает задать нужную вам.
Пример – Интерактивный режим

>>> 'Ёжик'.encode()
b'\xd0\x81\xd0\xb6\xd0\xb8\xd0\xba'
 
>>> 'Ёжик в тумане'.encode().decode()
'Ёжик в тумане'
 
>>> 'Ёжик в тумане'.encode().decode('utf-16')
'臐뛐룐뫐퀠₲苑菑볐냐뷐뗐'
Поясним полученные результаты и принцип работы этих методов.

Задача encode() – представить строку в виде объекта типа bytes (предваряется литералом b). Если знак относится к ASCII, то его байтовое представление будет выглядеть как оригинальный символ. В случае когда он выходит за пределы ASCII, то заменяется байтовым представлением (\x - эскейп-последовательность для обозначения 16-ричных чисел в языке Python).
Пример - интерактивный режим

>>> 'Python'.encode()
b'Python'
>>> 'Пайтон'.encode()
B'\xd0\x9f\xd0\xb0\xd0\xb9\xd1\x82\xd0\xbe\xd0\xbd'
Метод decode() преобразует последовательность байтов в привычную нам строку. Если задать другую кодировку, то можем получить либо неожиданный результат (как в последнем примере, где русские слова превратились в набор корейских иероглифов), либо ошибку UnicodeDecodeError, если в требуемой кодировке нет такого сочетания байтов или они выходят за ее границы.

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

5. Инструментарий для работы с кодировками

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

Перечислим те, которые вам могут понадобиться, а затем опробуем их на практике:

6. Практические примеры

Для усвоения материала попрактикуемся в применении изученного.

А. Функция ascii

Примеры – Интерактивный режим

>>> ascii(12)
'12'
>>> ascii('cat')
"'cat'"
>>> ascii('шапка')
"'\\u0448\\u0430\\u043f\\u043a\\u0430'"
Экранирование русского слова связано с тем, что ни один из символов не включен в стандартный набор знаков ascii (т.е. не имеет 1-байтового представления).

Б. Применение функции str для получения строк

Примеры – Интерактивный режим

>>> str(123)
'123'
>>> str(b'\xd0\x9f\xd0\xb0\xd0\xb9\xd1\x82\xd0\xbe\xd0\xbd', 'utf8')
'Пайтон'

В. Функция bytes

Превращает в объект bytes не только строки или числа, но и последовательности чисел (каждое из которых соответствует определенному символу).
Примеры – Интерактивный режим

>>> bytes('well done', 'ascii')
b'well done'
>>> bytes('Юта', 'utf16')
b'\xff\xfe.\x04B\x040\x04'
>>> bytes((80, 77, 99, 101))
b'PMce'

Г. Модуль unicodedata

Позволяет узнать имя любого юникод-символа, а также отобразить его по имени. С другими свойствами можете ознакомиться самостоятельно.
Примеры – Интерактивный режим

>>> import unicodedata
>>> unicodedata.name('Я')
'CYRILLIC CAPITAL LETTER YA'
>>> unicodedata.lookup('GIRL')
'????'
>>> unicodedata.lookup('Trigram for Heaven')
'☰'

Задачи по темам


Решаем задачи и отвечаем на вопросы по теме "Целые числа": работаем с типом данных int

Решаем задачи и отвечаем на вопросы по теме "Числа с плавающей точкой": работаем с типом данных float

Решаем задачи и отвечаем на вопросы по теме "Логический тип данных": работаем с типом данных bool

Строки как тип данных в Python. Основные методы и свойства строк. Примеры работы со строками, задачи с решениями.

Д. Отображение любого Юникод-символа

Функция chr() может принимать числа любой стандартной системы счисления (десятичной, двоичной, восьмеричной или шестнадцатеричной).

Функция ord() возвращает десятичное число, соответствующее символу юникод. Если требуется, мы это число можем преобразовать к любому иному основанию.
Примеры – Интерактивный режим

>>> chr(0o144)
'd'
>>> chr(0b1100100)
'd'
>>> chr(0x64)
'd'
>>> chr(100)
'd'
>>> ord('d')
100
Также попробуем немного экзотики. Если какой-то из символов не отображается, у вас нет шрифта в системе, который поддерживает эти знаки.
Примеры – Интерактивный режим

>>> chr(0x131B0)  # Египетский иероглиф
????
>>> chr(0x1F5A4)  # Черное сердечко
'????'
>>> chr(0x265E)  # Черный конь
'♞'
>>> chr(0x111E1)  # Сингальское число 1
????
>>> chr(0x4E78)  # Китайский иероглиф
'乸'
Воспользовавшись базой данных символов юникод, вы сумеете просмотреть все доступные варианты на сегодняшний день с указанием имени, кода, принадлежности.
10 АВГУСТА / 2020
Как вам материал?

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