Перейти к содержанию

Дополнительно. Кодировки и байты

Кодирование символов

Интерпретатор обращается к символу как к некому закодированному значению. Встроенные функции ord() и chr() возвращают код символа и символ, обозначающий код, соответственно.

## показать код символа
print(ord('H'))

## показать символ, соответствующий коду
print(chr(72))

Строку символов можно представить как набор кодов, которые можно получить с помощью цикла и наоборот.

## закодированная строка
for char in 'hello':
    print(ord(char))

## строка из списка кодов
out = ''
for code in [104, 101, 108, 108, 111, ]:
    out += chr(code)
print(out)

Соответствия кодов и символов были утверждены стандартом ASCII2.

## таблица ASCII2
for code in range(128):
    print(code, hex(code), chr(code))

## кириллица в Unicode
for code in range(1000, 1200):
    print(code, chr(code))

В интерпретаторе Python все символы хранятся в кодах Unicode. Unicode определяет связь между символом и некоторым числом, но не определяет, как символы будут хранится на жестком диске или передаваться по сети. Поэтому существуют форматы представления текста – как хранить коды unicode. Например, старший байт из трех идет первым или вторым? Наиболее распространенным является представление UTF-8.

Кодирование в UTF-8

Кодирование символа в UTF-8 происходит следующим образом.

  1. Сначала определяется количество байтов. Номер символа берется из стандарта Unicode.
  2. Затем устанавливается старший бит, в соответствии с необходимым количеством байт.
  3. В байте «xхххххx» – это количество значащих бит. Например в первом октете 7 значащих бит – 128 бит.
  4. Для строк адрес первого байта является адресом строки. Для чисел – знаком.
Шенадцатеричный диапазон номеров Десятичный диапазон номеров Требуемое количество октетов (байтов)
00000000-0000007F 0-127 1
00000080-000007FF 128-2047 2
00000800-0000FFFF 2048-65535 3
00010000-0010FFFF 2048-1114111 4
Количество байтов Значащий бит Шаблон
1 7 0xxxxxxx
2 11 110xxxxx 10xxxxxx
3 16 1110xxxx 10xxxxxx 10xxxxxx
4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

Байты и свойства

В Python есть тип данных, который позволяет хранить байты — bytes. Байты содержат коды в шестнадцатеричной системе в зависимость от количества хранимых байт.

bb = b'/xd1/x84'
print(bb)
print(type(bb))

Тип данных bytes имеет те же свойства, что и обычная строка, следовательно можно проводить операции индексации и сложения. bytes – неизменяемый тип данных.

bb = b'/xd0/xbf/xd1/x80/xd0/xb8/xd0/xb2/xd0/xb5/xd1/x82'
print(
    bb[0],
    bb.count(0xd0),
    b'/xd0/xbf' + b'/xd0/xb8'
    )

Задача №6. Байт в символ

Есть символы которые занимают несколько байт в памяти (символы кириллицы занимают по 2 байта). Имеем два байта символа кириллицы в UTF-8. Определить какой символ является полученным байтом?

bb = b'/xd1/x84'
Решение

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

Выведем на экран каждый байт по отдельности

print(bin(0xd1))  # 0b11010001
print(bin(0x84))  # 0b10000100

Значащие биты 10001 и 000100

code = 0b10001000100
print(code, hex(code), chr(code))

Формирование байт

При выводе байтов на терминал есть особенность. Латинские символы ASCII при выводе отображаются как символы, а не коды. Выведем на экран код символа «h» в шестнадцатеричной системе. При подстановке полученного значения в байт получим значения символов.

## код символа h в шестнадцатеричной системе
print(hex(ord('h')))  # 0x68

## сформируем строку из байтов
print(b'\x68\x69\x21')

Байтовые строки

Тип данных bytearray — хранит изменяемую последовательность байтов.

Можно создавать:

  • пустые строки,
  • пустые массивы байт,
  • упорядоченные последовательности,
  • строки из списков кодов,
  • последовательность байтов в указанной кодировке.
## байтовая строка
ba = bytearray(b'hello')
print(ba)

# пустая байтовая строка
print(bytearray())

# массив байт нулей
print(bytearray(6))

# последовательность байт до 16
print(bytearray(range(16))) 

# байтовая строка из списка кодов
print(bytearray([104, 101, 108, 108, 111, ]))

# создание байтовой строки в кодировке utf-8
print(bytearray('привет', encoding='utf-8'))

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

Для преобразования символов Unicode в последовательность байт используется метод строки encode().При использования разных кодировок получаются разные символы. Сейчас UTF-8 используется повсеместно, но раньше в разных ОС символы файлов отображались по-разному.

Закодировать строку 'привет' в байты:

print(bytearray('привет'.encode(encoding='utf-8’)))
print(bytearray('привет'.encode(encoding='cp866’)))
print(bytearray('привет'.encode(encoding='cp1251'))) 

Для декодирования байтовой строки в строку символов — метод строки decode().

ss = b'\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82'.decode(encoding='cp1251')
print(ss) 

Python может узнать о кодировке из файле, если указать в первой или второй строках файла следующий комментарий. Если такого комментария нет — то предполагается UTF-8.

# -*- coding: utf-8 -*-

Промежуточные итоги

  • Python может узнать о кодировке в данном файле, если указать в первой или второй строках файла следующий комментарий.
  • Если такого комментария нет – то предполагается UTF-8.
  • Каждый символ имеет код в Unicode. Каждый код может быть по-разному представлен в зависимости от кодировки.
  • Символы имеют представление в байтах или байтовых строках.
  • Файл является объектом в Python и представлен как поток байтов.
  • Байты файла можно читать и записывать в другие файлы.