ASCII-коды символов: таблица, как найти код и где он в UTF-8
В hex-редакторе вместо букв стоит последовательность 41 42 43, в документации протокола написано «разделитель полей: байт 0x1F», а в конце каждой строки лога мелькает 0D 0A. За каждым числом прячется конкретный символ, и соответствие между ними задаёт таблица ASCII. Умение быстро перевести символ в код и обратно пригождается чаще, чем кажется: при разборе файлов, отладке сетевых протоколов, написании парсеров и поиске невидимых символов в тексте. Разберём таблицу целиком: откуда взялись коды от 0 до 127, зачем нужна «пустая» половина, как найти код нужного символа за секунды и почему ASCII-файл в UTF-8 выглядит побайтово так же.
Что такое ASCII и почему коды от 0 до 127
ASCII расшифровывается как American Standard Code for Information Interchange, американский стандартный код для обмена информацией. Стандарт появился в 1960-х, когда текст передавали по телеграфным и телефонным линиям между телетайпами, и каждый лишний бит в такой передаче стоил денег и времени. Поэтому договорились о семи битах: два в седьмой степени дают ровно 128 кодовых позиций, которые нумеруются от 0 до 127. Семь бит, а не восемь, было осознанным решением: восьмой разряд на линиях связи отдавали под контроль чётности, то есть под обнаружение ошибок передачи.
Отсюда частое заблуждение, что ASCII «на 255 символов». Байт действительно вмещает значения от 0 до 255, но ASCII описывает только половину этого диапазона. Коды 128–255 появились позже в национальных кодировках, которые надстроили свои символы над таблицей, и в каждой такой кодировке верхняя половина своя: в CP1251 там кириллица, в CP850 псевдографика DOS, в ISO-8859-1 дополнительные латинские буквы. Сам ASCII как стандарт остаётся семибитным и заканчивается на коде 127. Историю стандарта и его роль сегодня мы подробно разбирали в статье про то, что такое ASCII и где он используется.
Управляющие символы 0–31 и 127: наследство телетайпов
Первые 32 кода и последний, код 127, не печатные. Это управляющие символы: они не изображают букву или знак, а командуют устройством. Придуманы они были для телетайпов, печатающих машинок, соединённых проводом, и в сокращениях до сих пор слышна механика: CR, carriage return, возврат каретки, это физическое движение печатающей головки в начало строки, а LF, line feed, прокрутка бумаги на одну строку вверх.
- LF, код 10: перевод строки. В Unix и macOS им заканчивается каждая строка текстового файла, в языках программирования он записывается как \n;
- CR, код 13: возврат каретки. Пара CR LF, байты 13 и 10 подряд, завершает строки в Windows и в сетевых протоколах: HTTP, SMTP, FTP;
- TAB, код 9: горизонтальная табуляция, в тексте выглядит как отступ фиксированной ширины;
- NUL, код 0: нулевой байт, терминатор строк в языке C, признак «строка закончилась»;
- ESC, код 27: начало управляющих последовательностей терминала, после него терминал ждёт команду: цвет текста, перемещение курсора, очистка экрана;
- BEL, код 7: звонок. В телетайпе при этом коде реально дзынькал колокольчик, привлекая внимание оператора;
- DEL, код 127: удаление. В перфоленте он не удалял ничего: пробивал все дырки в позиции, превращая символ в «пустое место», которое читатель просто пропускал.
Большинство из 32 управляющих кодов сегодня не используется, но выбросить их из таблицы было нельзя: формат задан стандартом, и устройства десятилетиями полагались на конкретные номера. Живых осталось несколько: LF, CR, TAB, ESC и NUL встречаются практически в каждом текстовом формате и протоколе. Остальные существуют как исторический слой, изредка всплывая в драйверах принтеров, старых терминалах и модемных командах.
Печатный диапазон 32–126: опорные точки таблицы
Всё, что можно напечатать на бумаге, живёт между кодами 32 и 126 включительно: 95 символов. Всю таблицу запоминать не нужно, достаточно пяти опорных точек, из которых достраивается остальное.
- пробел, код 32: самый первый печатный символ, у невидимого на вид знака есть полноценный номер;
- цифры 0–9, коды 48–57: идут подряд без пропусков;
- заглавные латинские A–Z, коды 65–90;
- строчные латинские a–z, коды 97–122;
- тильда ~, код 126: последний печатный символ таблицы.
Между цифрами, заглавными и строчными вклиниваются знаки препинания, скобки и математические знаки, поэтому внутри каждой группы коды идут без пропусков, а между группами стоят «разделители» вроде двоеточия и собаки. Отсюда берётся популярная задача с собеседований: перечислить диапазоны кодов для цифр и букв, и ответ укладывается в три пары чисел: 48–57, 65–90 и 97–122.
Есть и приятный побочный эффект при чтении hex-дампов: младший полубайт кода цифры совпадает с самой цифрой. Символ «7» имеет код 55, в шестнадцатеричной записи это 0x37, и последняя цифра 7 повторяет значение. Поэтому числа среди букв в дампе узнаются мгновенно: диапазон 0x30–0x39 это всегда цифры. Перевести любой код из hex в десятичную систему и обратно можно в конвертере систем счисления.
Как найти код символа: таблица, Python, JavaScript
Для разовой задачи быстрее всего готовая таблица. В таблице ASCII-кодов достаточно ввести символ, чтобы получить его код сразу в десятичной, шестнадцатеричной и двоичной системах, а обратный поиск по числу показывает сам символ: удобно проверять, что скрывается за байтом из дампа. Посмотреть тот же текст в виде байтов целиком позволяет переводчик в двоичный код: он показывает каждый символ строки в виде битового представления.
Когда искать нужно много и часто, выручает одна строка кода. В Python за код символа отвечает функция ord(), за обратное преобразование chr():
>>> ord('A')
65
>>> ord('a')
97
>>> ord('0')
48
>>> ord(' ')
32
>>> chr(97)
'a'
>>> hex(ord('A'))
'0x41'В JavaScript у строки есть метод charCodeAt(), который возвращает код символа по индексу, и метод codePointAt(), учитывающий символы за пределами базовой плоскости Unicode. Для всего ASCII-диапазона оба дают одинаковый результат, разница проявляется на символах с кодами выше 65535.
'A'.charCodeAt(0) // 65
'a'.charCodeAt(0) // 97
'0'.charCodeAt(0) // 48
'Я'.charCodeAt(0) // 1071: кириллица лежит за пределами ASCII
'Я'.codePointAt(0) // 1071Разница методов важна на «больших» символах, например на эмодзи: charCodeAt() возвращает значение кодовой единицы UTF-16, и для таких символов это половинка суррогатной пары, число вроде 55357, а codePointAt() возвращает настоящий код Unicode, например 128512. В задачах с латиницей, цифрами и кириллицей оба метода работают одинаково, потому что все эти символы укладываются в одну кодовую единицу.
Почему A и a различаются ровно на 32
Заглавная A имеет код 65, строчная a имеет код 97, и разница между ними ровно 32. Это не совпадение, а инженерное решение комиссии по стандарту: число 32 это вес пятого бита байта, поэтому перевод регистра сводится к переключению одного разряда. В двоичной записи 65 это 1000001, а 97 это 1100001: отличается только разряд с весом 32. Для телетайпной электроники такое упрощение было ценным, а сегодня оно продолжает работать в каждом языке программирования.
Практических следствий несколько. Перевод латинской буквы в верхний регистр это вычитание 32 из кода, проверка «строчная ли буква» это вопрос «лежит ли код между 97 и 122». В шестнадцатеричной записи пары отлично видны: 0x41 и 0x61, 0x5A и 0x7A, отличается ровно одна цифра записи. Есть и побочный эффект в сортировке: если упорядочить строки по кодам байтов, все заглавные буквы окажутся раньше строчных, потому что 65 меньше 97. Именно поэтому «Zebra» в побайтовом порядке стоит перед «apple», и именно поэтому нормальные алгоритмы сортировки учитывают регистр отдельно, а не полагаются на коды напрямую.
Тот же приём объясняет и цифры: код символа «5» минус код нуля даёт само число пять, 53 минус 48 равно 5. Функции преобразования строк в числа внутри выполняют именно это вычитание, поэтому посимвольный разбор десятичного числа пишется в три строки и не требует таблиц.
ASCII внутри UTF-8: первые 128 байтов совпадают
UTF-8 кодирует символы переменным числом байтов: от одного до четырёх. Правило разбиения простое. Символы с кодами 0–127 записываются одним байтом, значение которого равно самому коду. Всё, что выше, занимает два, три или четыре байта, и каждый такой байт начинается с битов 10 или 11, по которым его сразу отличаешь от ASCII-байта.
Следствие: любой файл, содержащий только ASCII-символы, в UTF-8 выглядит побайтово так же, как в «чистом» ASCII. Текст латиницей открывается любой кодировкой из семейства UTF без потерь, и наоборот: старые программы, рассчитанные на английские буквы и цифры, продолжают работать с UTF-8-текстом, пока тот не выходит за латиницу. Как только появляется кириллица или эмодзи, начинается разница: заглавная русская Я имеет код Unicode 1071, и в UTF-8 она занимает два байта D0 AF вместо одного.
Именно эта совместимость и есть главная причина, по которой UTF-8 победил в интернете: он не ломает ни один старый формат, пока текст англоязычный, и при этом умеет любой язык мира. Подробный разбор устройства UTF-8, многобайтовых последовательностей и BOM есть в статье про UTF-8 и Unicode.
Кириллицы в ASCII нет: что с ней делали раньше
ASCII создавался для английского языка, и ни одной русской буквы в таблице нет. Кириллицу решали добавлять поверх: когда восьмибитные байты стали нормой, верхнюю половину таблицы, коды 128–255, заняли национальные алфавиты. Для русского языка таких кодировок было несколько, и файл, сохранённый в «неправильной» из них, до сих пор открывается кракозябрами.
КОИ-8, кодировка советских и российских Unix-систем, располагала русские буквы так, чтобы при потере старшего бита текст превращался хотя бы в похожие по звучанию латинские буквы. Это была защита от семибитных каналов: даже с испорченным восьмым битом письмо оставалось угадываемым. Windows-1251, кодировка эпохи Windows 95 и XP, собрала кириллицу подряд в кодах 192–255, и именно её до сих пор подразумевают под «ANSI» в русскоязычных программах. Ещё была CP866 из мира DOS, с псевдографикой и другим порядком букв.
Сегодня все эти кодировки существуют как легаси, но проверять кодировку файла по-прежнему нужно, особенно для данных из старых систем: выгрузок из 1С, файлов бирж, архивов писем. Определитель кодировки показывает предполагаемую кодировку текста по самим байтам, и это первый шаг при любых кракозябрах: почти всегда файл оказывается в CP1251 или KOI8-R, а открывают его как UTF-8.
Практика: проверяем, что файл чистый ASCII
Зачем вообще проверять файл на «чистоту» ASCII? Причины практические. Конфигурационные форматы вроде старых .ini и многие протоколы ожидают только английские буквы, цифры и знаки, и одна русская буква в комментарии способна уронить парсер. Файлы ключей и PEM-сертификатов тоже должны быть побайтово ASCII: невидимый символ из другой кодировки ломает разбор так, что ошибка выглядит как «неверный формат ключа» при визуально безупречном файле. Логи, которые парсит автоматика, тоже проще держать в ASCII.
Проверка сводится к одному условию: все байты файла меньше 128. В Python это две строки.
data = open('config.ini', 'rb').read()
print(all(b < 128 for b in data))
# True: файл чистый ASCII
# False: внутри есть байты из другой кодировки или UTF-8
# где именно сломано:
bad = [i for i, b in enumerate(data) if b > 127]
print(bad[:10])Если проверка выдала False, список номеров байтов покажет, где именно: первый байт со значением 128 и выше укажет на первый «неанглийский» символ. Невидимые знаки этот фильтр тоже ловит: неразрывный пробел имеет код Unicode 160 и в UTF-8 занимает два байта C2 A0, а метка BOM добавляет в начало файла три байта EF BB BF. Внешне файл выглядит идеально, а проверка показывает, что «якобы ASCII» содержит посторонние байты.
Типичные ошибки
- путают десятичную и шестнадцатеричную запись. Видят в дампе 0x41 и ищут код 41 в таблице, находят закрывающую скобку. Прежде чем искать, проверьте префикс: 0x41 это 65, то есть заглавная A;
- считают, что ASCII содержит 255 или 256 символов. В стандарте 128 кодов, из них печатных 95, а верхняя половина байта принадлежит национальным кодировкам;
- забывают, что пробел тоже символ. У него есть полноценный код 32, и в подсчёте длины строки он учитывается наравне с буквами;
- приписывают ASCII кириллицу. Кода «русская A» в таблице нет: число 192 свободно в ASCII, а в CP1251 это уже заглавная А кириллическая;
- используют charCodeAt() для не-ASCII символов и удивляются числам вроде 1071. Это не ошибка метода: кириллица действительно живёт за пределами первых 128 кодов;
- ищут «код буквы ё» в основных диапазонах. Даже в CP1251 буква ё выбивается из общего ряда и лежит отдельно от остальных кириллических букв.
Частые вопросы
Какой код у буквы A и у буквы a?
Заглавная латинская A имеет код 65, строчная a имеет код 97. Всё семейство запоминается тремя диапазонами: цифры 48–57, заглавные 65–90, строчные 97–122. Разница между регистрами всегда ровно 32, поэтому из заглавной буквы строчная получается простым сложением, а обратно вычитанием.
Чем отличаются ASCII и Unicode?
ASCII это таблица из 128 символов с кодами от 0 до 127. Unicode гораздо шире: в нём больше 150 тысяч символов, а коды записывают в форме U+номер, например U+0410 для русской А. UTF-8 это способ записи Unicode-кодов байтами, и первые 128 символов Unicode в этой записи совпадают с ASCII побайтово, поэтому совместимость сохранилась.
Как узнать код символа, которого нет в таблице ASCII?
Для кириллицы, эмодзи и любых других символов нужен уже Unicode-код. В Python тот же ord() вернёт его: ord() для русской Я даёт 1071, это U+042F. В JavaScript для таких символов берут codePointAt(), а не charCodeAt(), чтобы получить полный код, а не половинку суррогатной пары.
Почему в файлах Windows 13 и 10 вместо одного перевода строки?
Это пара CR LF, наследие телетайпов: сначала каретка возвращалась в начало строки, затем бумага сдвигалась на строку вверх. Windows унаследовал пару целиком, Unix и macOS используют только LF, а классический Mac OS до версии X только CR. В сетевых протоколах вроде HTTP пара CR LF обязательна независимо от операционной системы.
Что такое расширенный ASCII?
Так называют семейство восьмибитных кодировок, где коды 128–255 заняты дополнительными символами: псевдографикой в CP866 и CP850, кириллицей в CP1251 и KOI8-R, дополнительными латинскими буквами в ISO-8859-1. Единой таблицы «расширенного ASCII» не существует: у каждого производителя и региона своя верхняя половина, поэтому файл в одной такой кодировке в другой читается неправильно.
Таблица ASCII-кодов
Полный справочник ASCII: коды 0–127 в DEC, HEX, OCT и BIN с поиском по символу или коду
Двоичный переводчик
Конвертируйте текст в двоичный код (0 и 1) и обратно с побайтовым разбором
Конвертер систем счисления
Конвертируйте числа между двоичной, восьмеричной, десятичной, шестнадцатеричной и произвольной системами
Определение кодировки
Определите кодировку текста или файла онлайн: UTF-8, UTF-16, ASCII, Windows-1251, KOI8-R. Уверенность определения, разбор байтов и превью «как выглядит текст» в разных кодировках