Модуль 5 · Строки

Справочник по Python · модуль 5 · строки

Глава 5.1

5.1

Unicode, ord()
и chr()

Строка Python хранит символы Unicode, и у каждого символа есть номер — кодовая точка. ord() получает номер символа, chr() выполняет обратное преобразование. Через эти номера работают сравнение строк, сортировка и любые посимвольные вычисления.

Категория
Строки и Unicode
Уровень
Базовый → продвинутый
Связанные темы
Строки, числа, кодировки, сортировка
Зачем это знать
Связывать символы с числовыми кодовыми точками

§1Зачем символу номер

Компьютер не хранит буквы. Он хранит числа, а буква — это договорённость о том, какое число как показывать на экране. Unicode и есть такая договорённость: единая таблица, где каждому символу мира назначен свой номер.

Знать этот номер нужно не из любопытства. Через него объясняются вещи, которые иначе выглядят необъяснимо:

  1. почему "Я" < "а" даёт True, хотя «я» — последняя буква алфавита;
  2. почему sorted() ставит слова с заглавной буквы раньше остальных;
  3. как проверить, что символ — цифра, не сравнивая его с десятью вариантами;
  4. почему у русской буквы len() равен единице, а в файле она занимает два байта.

Все четыре ответа — в этой главе.

§2Символ и кодовая точка

Кодовая точка — номер символа в таблице Unicode. Его записывают в двух видах: как U+0041 (шестнадцатеричная запись, принятая в стандарте) и как обычное десятичное число 65, с которым работает Python.

СимволКодовая точкаДесятичное число
0U+003048
AU+004165
aU+006197
ЯU+042F1071
€U+20AC8364

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

Схемадве функции — два направления
       ord()
"A"  ─────────→  65
     ←─────────
       chr()

§3ord(): символ → число

ord(char) принимает строку длиной один символ и возвращает её кодовую точку целым числом.

main.pyтри разных алфавита
print(ord("A"))
print(ord("Я"))
print(ord("€"))
Выводцелые числа
65
1071
8364

Название функции — от английского ordinal, «порядковый номер». Возвращается всегда int, с которым можно делать обычную арифметику.

§4chr(): число → символ

chr(number) делает обратное: принимает целое число и возвращает строку из одного символа с таким номером.

main.pyобратное направление
print(chr(65))
print(chr(1071))
print(chr(8364))
Выводстроки длиной один
A
Я
€

Результат — именно строка, а не «символ»: отдельного типа для одного символа в Python нет. Поэтому к результату chr() применимы все строковые методы.

§5Обратимость преобразования

Две функции строго обращают друг друга: chr(ord(x)) всегда возвращает исходный символ.

main.pyпереход туда и обратно
symbol = "Ж"
code_point = ord(symbol)
restored = chr(code_point)

print(code_point)
print(restored)
print(restored == symbol)
Выводсимвол восстановлен без потерь
1046
Ж
True
Пара функций, а не одна

Легко перепутать направление. Подсказка в названиях: ord даёт ordinal — номер, значит на выходе число. chr даёт chraracter — символ, значит на выходе строка.

§6Ровно один символ

Требование «строка длиной 1» соблюдается строго. Ни пустая строка, ни строка из двух букв не подойдут.

main.pyпередано два символа
print(ord("AB"))
Ошибкафункция ждёт один символ
Traceback (most recent call last):
  File "main.py", line 1, in <module>
    print(ord("AB"))
          ^^^^^^^^^
TypeError: ord() expected a character, but string of length 2 found

Пустая строка даёт то же исключение со словами string of length 0. Чтобы получить коды нескольких символов, строку перебирают — об этом §9.

§7Допустимый диапазон chr()

Unicode охватывает кодовые точки от 0 до 0x10FFFF — то есть до 1 114 111 включительно. Число за этой границей символа не обозначает.

main.pyна единицу больше предела
print(chr(0x110000))
Ошибкавне пространства Unicode
ValueError: chr() arg not in range(0x110000)

Разница в типах исключений не случайна: TypeError означает «передано не то что нужно», ValueError — «тип верный, но значение недопустимо». Отрицательное число тоже даёт ValueError.

§8Кодовая точка в литерале

Символ можно записать прямо в строке, не вызывая chr(), — через escape-последовательность с шестнадцатеричным кодом.

main.pyescape-последовательности Unicode
latin = "\u0041"
cyrillic = "\u042f"
emoji = "\U0001F642"

print(latin, cyrillic, emoji)
Выводготовые символы
A Я 🙂
ЗаписьСколько цифрПример
\uровно 4"\u042F" → Я
\Uровно 8"\U0001F642" → 🙂
\xровно 2"\x41" → A

Количество цифр обязательное: "\u42F" — синтаксическая ошибка, нужно "Я" с ведущим нулём. Регистр самих цифр значения не имеет.

§9Коды всей строки

ord() работает с одним символом, поэтому для строки её перебирают.

main.pyкод каждой буквы
text = "Код"
codes = [ord(symbol) for symbol in text]

print(codes)
Выводтри кодовые точки
[1050, 1086, 1076]

Обратная сборка требует join(): chr() даёт отдельные строки, и их нужно склеить.

main.pyиз списка кодов обратно в строку
codes = [1050, 1086, 1076]
print("".join(chr(code) for code in codes))
Выводстрока восстановлена
Код

Метод join() подробно разбирается в главе 5.9. Здесь достаточно знать, что он склеивает набор строк в одну.

§10Сравнение строк

Теперь можно ответить на первый вопрос из §1. Операторы < и > сравнивают строки посимвольно, а каждую пару символов — по кодовым точкам.

main.pyрегистр решает исход
print("A" < "a")
print(ord("A"), ord("a"))
Вывод65 меньше 97
True
65 97

Никакой «алфавитности» здесь нет — есть только числа. Заглавная A меньше строчной a просто потому, что её номер меньше.

§11Сортировка и регистр

Из того же правила следует поведение, которое регулярно принимают за ошибку сортировки.

main.pyтри слова по алфавиту
words = ["Яблоко", "арбуз", "Ананас"]
print(sorted(words))
Вывод«арбуз» оказался последним
['Ананас', 'Яблоко', 'арбуз']

Причина видна по кодам: ord("А") — 1040, ord("Я") — 1071, а ord("а") — уже 1072. Все заглавные кириллические буквы идут до всех строчных, поэтому «арбуз» с маленькой буквы проигрывает даже «Яблоку».

Как получить привычный порядок

Сортировать по приведённой к одному регистру форме: sorted(words, key=str.lower). Тогда сравниваются ананас, арбуз, яблоко, и результат совпадает с ожиданиями. Аргумент key — тема главы 6.10.

§12Арифметика по кодам

Раз кодовые точки — числа, с ними работают обычные операции. Цифры в Unicode идут подряд от 0 (48) до 9 (57), и это даёт две полезные записи.

main.pyпроверка и превращение цифры в число
symbol = "7"

print(ord("0") <= ord(symbol) <= ord("9"))
print(ord(symbol) - ord("0"))
Выводэто цифра, её значение — 7
True
7

Буквы алфавита тоже идут подряд, поэтому так же вычисляют номер буквы в алфавите.

main.pyкакая по счёту буква
print(ord("г") - ord("а") + 1)
Вывод«г» — четвёртая
4

В обычном коде для проверки цифры удобнее готовый метод symbol.isdigit() из главы 5.7. Приём с кодами нужен там, где важно само числовое расстояние между символами.

Буква «ё» стоит в стороне

В кириллице подряд идут только а–я (1072–1103). Буква ё имеет код 1105 и в этот ряд не попадает, поэтому формула «номер буквы» на ней ломается.

§13Сдвиг символов

Классическое применение пары функций — сдвинуть каждый символ на несколько позиций. На этом построен шифр Цезаря.

main.pyкаждый символ на одну позицию вперёд
text = "abc"
result = ""

for symbol in text:
    result += chr(ord(symbol) + 1)

print(result)
Выводсдвиг на единицу
bcd
  1. ord(symbol) переводит символ в число.
  2. К числу прибавляется сдвиг — получается кодовая точка соседнего символа.
  3. chr() возвращает результат обратно в строку.

Настоящий шифр Цезаря требует ещё и «заворачивания» алфавита: после z должна снова идти a, а не следующий по таблице символ {. Для этого сдвиг берут по остатку от деления на длину алфавита.

§14Символ — это не байт

Последний вопрос из §1. Кодовая точка — номер символа в таблице, а не то, как символ лежит в файле или в сети. Хранение — отдельный шаг, и там один символ вполне может занимать несколько байтов.

main.pyодин символ, два байта
letter = "Я"

print(len(letter))
print(len(letter.encode("utf-8")))
Выводдлина в символах и в байтах
1
2

Для Python это одна буква, для файла — два байта. Перевод символов в байты и обратно — тема главы 5.4.

§15Частые ошибки

Что написаноЧто произойдётКак правильно
ord("AB")TypeError: длина аргумента равна 2передать один символ или перебрать строку
ord("")TypeError: длина аргумента равна 0проверить, что строка не пуста
ord(65)TypeError: ожидается строкаchr(65) — направление перепутано
chr("65")TypeError: ожидается целое числоchr(65) без кавычек
chr(0x110000)ValueError: вне Unicodeкодовая точка от 0 до 1 114 111
"\u42F"SyntaxError: нужно ровно четыре цифры"\u042F"
ждать алфавитный порядок от sorted()заглавные окажутся раньше строчныхsorted(words, key=str.lower)
считать кодовую точку байтомтеряется различие между символом и его кодировкойдля байтов применять encode()

§16Проверьте себя

Что возвращает ord()?

Целое число — кодовую точку единственного переданного символа Unicode.

Почему ord("ab") вызывает ошибку?

Функция принимает ровно один символ, а в строке их два. Для строки нужен перебор.

Почему "Я" < "а" истинно?

Сравниваются не буквы алфавита, а кодовые точки: у Я это 1071, у а — 1072. Все заглавные кириллические буквы идут раньше всех строчных.

Чем TypeError у ord() отличается от ValueError у chr()?

TypeError означает, что передано не то, что нужно, а ValueError — что тип верный, но значение вне допустимого диапазона.

Как получить числовое значение символа-цифры?

ord(symbol) - ord("0"): цифры идут в таблице подряд, поэтому разность даёт само число.

Почему len("Я") равен 1, а в файле буква занимает два байта?

Кодовая точка — номер символа в таблице, а не способ его хранения. Перевод в байты выполняет encode().

§17Шпаргалка

Символ → числоord("A") # 65
Число → символchr(65) # "A"
Коды строки[ord(ch) for ch in text]
Обратно в строку"".join(chr(c) for c in codes)
Литерал"\u042F" # "Я"
Цифра в числоord(ch) - ord("0")
Сдвиг символаchr(ord(ch) + 1)
Границы0 … 1_114_111

Итог главы

ord() переводит один символ в его кодовую точку, chr() выполняет обратное преобразование, и вместе они полностью обратимы. Через кодовые точки объясняется поведение сравнения и сортировки строк: Python сравнивает номера символов, а не буквы алфавита. Кодовая точка при этом описывает символ в таблице Unicode, но не способ его хранения — за это отвечают кодировки.

Глава 5.1 · Unicode · Справочник по Python Автор: Макаров Максим Николаевич