ASCII: что это и где используется
Открываете текстовый редактор и видите в начале файла незнакомые символы. Или получаете от коллеги письмо, где вместо текста цепочка символов вроде #5$#@!$&%. Или смотрите дамп файла и видите 0D 0A в конце каждой строки. Всё это следы одного стандарта, который появился раньше интернета, персональных компьютеров и даже графических мониторов. Стандарт называется ASCII.
Что такое ASCII и откуда он взялся
ASCII расшифровывается как American Standard Code for Information Interchange, американский стандартный код для обмена информацией. Первая версия была опубликована в 1963 году как ASA X3.4-1963, позже стандарт переиздавался в редакциях ANSI X3.4-1968, -1977 и -1986.
История началась ещё раньше. В мае 1961 года инженер IBM Боб Бемер предложил Американской ассоциации по стандартам создать единый код для обмена информацией между вычислительными машинами. Задача была практической: у каждого производителя была своя таблица символов, и данные приходилось перекодировать вручную при переносе с одной машины на другую.
Здесь важно понимать контекст. Стандарт создавался в мире телетайпов и перфокарт, а не в мире текстовых редакторов. Отсюда и главное техническое решение: код семибитный. Семь бит хватает для 128 значений, а восьмой бит на перфоленте можно было отдать под проверку чётности, то есть под обнаружение ошибок при передаче. Отправлять восьмой бит по линии было дорого, и от него отказались.
Обратите внимание на формулировку: восьмой бит не был частью символа ASCII. Это отдельный механизм контроля ошибок, использовавшийся на телеграфных линиях. На восьмибитных компьютерах, где проверки чётности не было, этот бит обычно просто выставлялся в ноль. Путаница между «ASCII это семь бит» и «ASCII это восемь бит» до сих пор встречается, хотя в стандарте всё однозначно.
128 символов и как устроена таблица
Семь бит дают ровно 128 кодовых позиций, от 0 до 127. Таблица разделена на четыре группы по 32 символа, и деление это не условность: в каждой группе ровно 16 печатных и 16 непечатных символов, поэтому в коде проще ориентироваться, чем в общей таблице на 256 позиций.
- 0x00–0x1F, коды с 0 по 31: управляющие символы. Их 32, и на них приходится весь «невидимый» синтаксис текстовых форматов;
- 0x20–0x3F, коды с 32 по 63: пробел, знаки препинания и цифры от 0 до 9;
- 0x40–0x5F, коды с 64 по 95: знак @, заглавные латинские буквы A–Z и часть знаков препинания;
- 0x60–0x7F, коды с 96 по 127: строчные латинские буквы a–z, остальные знаки препинания и служебный код 127.
Печатных символов в таблице 95, и это числа с 32 по 126 включительно. Пробел имеет код 32, восклицательный знак 33, цифра 0 код 48, заглавная A код 65, строчная a код 97, а последний печатный символ, тильда, код 126. Код 127 не печатный: это DEL, удаление, оставшееся от телетайпной эпохи.
Символ Десятичное Шестнадцатеричное Двоичное
пробел 32 0x20 00100000
! 33 0x21 00100001
0 48 0x30 00110000
A 65 0x41 01000001
Z 90 0x5A 01011010
a 97 0x61 01100001
z 122 0x7A 01111010
~ 126 0x7E 01111110Перевести любое число в шестнадцатеричную или двоичную форму можно через конвертер систем счисления, а посмотреть, как строка выглядит побайтово, позволяет переводчик в двоичный код.
Управляющие символы: CR, LF, TAB, ESC
Половина таблицы приходится на управляющие символы, и именно они объясняют, почему в текстовых файлах встречаются невидимые конструкции. Каждый из них задаёт не букву, а действие.
- NUL, код 0: нулевой байт, признак конца строки в C и основа для строк с нулевым терминатором;
- TAB, код 9: горизонтальная табуляция, переход к позиции с шагом 8;
- LF, код 10: перевод строки, тот самый символ, который в языках программирования записывают как \n;
- VT, код 11: вертикальная табуляция;
- FF, код 12: перевод страницы или очистка экрана;
- CR, код 13: возврат каретки, который записывают как \r;
- ESC, код 27: экранирование, начало управляющих последовательностей терминала;
- DEL, код 127: удаление символа.
Почему перевод строки записывается именно как \n, если его код равен 10? Это не совпадение и не путаница. Значение \n в языках C, Java, JavaScript и Python исторически соответствует коду LF, то есть десятичному 10 в таблице ASCII. Управляющая последовательность буквально означает «символ с номером 10», и то, что десятичное запись выглядит как n, объясняется тем, что LF десятичная десятка, а буква n рядом с ней стоит в старой системе счисления счислений, использовавшейся в ранних системах.
Второй управляющий символ, который встречается чаще всего, это CR с кодом 13, записываемый как \r. Назначение у него буквальное: в телетайпном устройстве CR возвращала каретку в начало строки, а LF сдвигала бумагу на одну строку вверх. Это два отдельных механизма, и в системах, где печатающая машинка была единственным выводом, они вызывались по отдельности.
Отсюда две договоренности, которые живут до сих пор. В системах Unix конец строки обозначается одним символом LF, то есть 0x0A. В системах Windows используется пара CR LF, то есть 0x0D 0x0A, где CR возвращает каретку, а LF опускает её на новую строку. Из-за этого файлы, созданные в одной системе и прочитанные в другой, иногда выглядят как одна длинная строка: символ LF пролетает мимо, потому что курсор не возвращается в начало.
Unix (LF): 68 65 6C 6C 6F 0A hello.
Windows (CR LF): 68 65 6C 6C 6F 0D 0A hello.
Старый Mac (CR): 68 65 6C 6C 6F 0D hello.
0A = перевод строки, 0D = возврат каретки
В тексте видно: "hello." на одной строке, "hello" с последующим переводомЕщё один управляющий код, который встречается чаще, чем кажется, это TAB. В текстовых форматах с выравниванием, таких как TSV или формат Makefile, символ с кодом 9 служит разделителем колонок или отступом. Поэтому файл, где вместо табуляции стоят пробелы, может разъезжаться при открытии в другой программе.
ASCII внутри современных кодировок
Сейчас ASCII используется почти исключительно как фундамент. Первая его большая заслуга в том, что первые 128 кодовых позиций таблицы Unicode совпадают с ASCII символ в символ. Именно поэтому ASCII считается подмножеством Unicode, и именно поэтому UTF-8 оказался обратно совместимым с ASCII.
Совместимость выглядит так. Символ с кодом меньше 128 в UTF-8 записывается ровно одним байтом с тем же значением, что и в ASCII. Символ с кодом 128 и выше записывается двумя, тремя или четырьмя байтами. Получается, что файл, в котором нет ни одного символа выше U+007F, побайтово одинаков в ASCII и в UTF-8, а программа, читающая только ASCII, продолжит работать с новыми файлами без изменений. Подробнее о том, как устроено преобразование кодовых точек в байты, рассказано в статье про UTF-8 и Unicode.
- ASCII это подмножество Unicode: первые 128 позиций совпадают один в один;
- UTF-8 обратно совместим с ASCII: символы ниже U+0080 занимают ровно один байт;
- Windows-1251 и KOI8-R тоже сохраняют ASCII в первых 128 позициях, добавляя кириллицу в верхней части таблицы;
- любая современная кодировка, претендующая на совместимость с ASCII, держит латиницу, цифры и базовую пунктуацию на тех же местах.
Где ASCII встречается сегодня
Стандарт, которому шестьдесят лет, не исчез и не устарел. Он живёт в местах, где важна не красота записи, а предсказуемость.
- протоколы и заголовки. HTTP-заголовки, большинство команд SMTP, формат DNS построены на ASCII, и это позволяет разбирать их побайтово без сложных парсеров;
- исходный код и конфигурационные файлы. Скрипты, Makefile, requirements.txt, конфиги nginx и systemd пишутся в ASCII, чтобы работать одинаково в любой среде;
- имена файлов. Ограничение ASCII в именах избавляет от проблем с кодировками при переносе между системами;
- форматы данных. CSV, JSON Schema, robots.txt, sitemap.xml используют ограниченный набор символов;
- ASCII-арт. Баннеры из символов, рисунки в комментариях к коду и в README, оформление вывода в терминале;
- аппаратные протоколы. Многие микроконтроллеры и промышленные устройства работают с ограниченными наборами символов по UART;
- учебные материалы. Кодировка Морзе, например, использует только латинские буквы, цифры и знаки препинания, и потому прекрасно ложится на таблицу ASCII.
Сделать баннер из символов для вывода в терминале или для README можно через генератор ASCII-арта. Там же рядом лежит переводчик азбуки Морзе, который работает по той же логике замены символов, только набор знаков другой.
Кириллицы в ASCII нет, и вот что из этого следует
Главное практическое ограничение: в таблице 128 символов нет ни одной русской буквы. Латиница, цифры, пунктуация и управляющие коды, то есть всё, что там есть. Исторически это нормально, потому что стандарт создавался в англоязычной среде, но сегодня из этого вырастают конкретные проблемы.
Первый вариант, добавить кириллицу в саму таблицу, был отвергнут: стандарт на то и стандарт, что его не переделывают на ходу. Второй вариант, расширить таблицу в восьмибитном варианте, оказался удачным, и так появились кодировки вроде Windows-1251 и KOI8-R, где первые 128 позиций остались ASCII, а кириллица заняла верхнюю часть таблицы.
Код Символ ASCII на этом месте
0xC0 А не определён
0xD8 Ё не определён
0xDF Я не определён
0xE0 а не определён
0xF8 ё не определён
0xFF я не определён
Позиции 0x00–0x7F в Windows-1251 совпадают с ASCII один в один.Третий вариант, сделать таблицу на 1 114 112 позиций и хранить символы в UTF-8, стал тем решением, к которому пришли все. Но у него появилось побочное следствие: если программа читает файл в ASCII, она не просто «не увидит» кириллицу, она может испортить файл целиком. Одна русская буква в UTF-8 занимает два байта, и если один из них приходится на управляющий код, получается совсем неожиданный эффект.
Неразборчивые символы в файле: откуда они берутся
Отдельная тема, достойная отдельного разбора, это файлы, где вместо текста видны странные значки. Причин несколько, и различать их полезно, потому что лечатся они по-разному.
- файл в UTF-16, прочитанный как однобайтовый. Между символами появляются нули, потому что в UTF-16 каждый символ занимает два байта;
- файл в UTF-8 с BOM, где первые три байта EF BB BF интерпретированы как отдельные символы и дают невидимый хвост в начале;
- файл в UTF-8 без BOM, прочитанный как windows-1251. Получается классическая картинка из двух латинских букв на месте одной русской;
- двоичный файл, открытый в текстовом редакторе. Там действительно нет текста, есть байты;
- файл, где вместо переводов строк оказались неразрывные пробелы или неразрывный дефис, вставленные при копировании из веба.
Определить кодировку файла по первым байтам и сравнить варианты чтения помогает детектор кодировки.
Как читать дамп и не путать байты с символами
Дамп файла показывает байты, а не символы, и это главный источник непонимания. В дампе каждый байт занимает две шестнадцатеричные цифры, потому что в байте ровно 256 значений, а шестнадцатеричная цифра даёт 16. Один байт всегда умещается ровно в пару hex-цифр, и это позволяет читать дамп на глаз.
Чтобы разложить такие значения на составляющие, помогает инструмент разбора HEX в RGB: он показывает десятичное значение каждого байта и его долю в цвете, а заодно наглядно демонстрирует правило «один байт равен двум hex-цифрам».
И отдельно про то, как в дампе выглядят escape-последовательности. Если в тексте программы написано \t, это не два символа «обратный слэш и t», а один символ с кодом 9. То же самое с \n, \r и любым другим именованным кодом. Разница видна на уровне байтов: в файле escape-последовательность занимает два байта, а управляющий символ один.
ASCII против Unicode: в чём разница
Путаница между этими терминами возникает постоянно, хотя они отвечают на разные вопросы. ASCII это конкретная таблица на 128 символов с конкретным набором управляющих кодов. Unicode это соглашение о том, какие символы вообще существуют в мире, с номерами от U+0000 до U+10FFFF. UTF-8 это способ упаковать эти номера в байты.
- ASCII: 128 символов, 7 бит, только латиница и управляющие коды, стандарт 1963 года;
- Unicode: более 1,1 миллиона кодовых позиций, все письменности мира, стандарт с 1991 года;
- UTF-8: кодировка, где символ занимает от 1 до 4 байт в зависимости от номера;
- ASCII входит в Unicode как подмножество, поэтому любая программа, работавшая с ASCII, работает и с UTF-8 для латиницы.
Отсюда практический вывод: если задача в том, чтобы понять, что за символы в конкретном файле, начинать стоит с определения кодировки, а не с гадания по внешнему виду. Стандарт ASCII полезен как точка отсчёта, от которой удобно считать.
Типичные ошибки
- считать, что ASCII это восемь бит. В стандарте семь бит, а восьмой использовался под проверку чётности на перфоленте;
- путать 95 и 128. Печатных символов 95, с кодами от 32 до 126, а управляющих 33, с кодами 0–31 и 127;
- забыть про регистр в кодах букв. Заглавная A это 65, строчная a это 97, разница ровно 32;
- полагать, что \n это буква n. Это управляющий символ с кодом 10, и в дампе он занимает один байт, тогда как escape-последовательность в исходном коде занимает два;
- ожидать, что ASCII-файл с кириллицей будет работать. Кириллицы в стандарте нет вообще;
- писать в HTML код символа, не проверив, что он помещается в ASCII. Символы с кодом выше 127 в HTML записываются как числовые ссылки или как именованные сущности, иначе разметка ломается.
Частые вопросы
Чем ASCII отличается от UTF-8?
Размером и возможностями. ASCII описывает 128 символов в семи битах, из которых 95 печатных, и ничего кроме латиницы. UTF-8 описывает более миллиона кодовых точек Unicode, используя от одного до четырёх байт на символ. При этом ASCII полностью содержится в UTF-8, и текст, где нет символов выше U+007F, в обеих кодировках выглядит одинаково.
Почему в ASCII только 128 символов?
Так сложилось исторически и технически. Стандарт создавался для телеграфных линий и перфокарт, где каждый символ стоил отдельный бит времени передачи, и экономия была важнее удобства. Восьмой бит отводился под проверку чётности при передаче, поэтому в сам символ он не входил. Добавление новых символов в таблицу обошлось бы в изменение стандарта, а это оказалось проще сделать через отдельные расширения.
Что значит 0x20 и почему это не 32?
0x20 это просто запись того же самого числа в шестнадцатеричной системе: 2 умножить на 16 плюс 0, итого 32 в десятичной. Разные записи одного числа. В дампах файлов принято показывать байты в hex, потому что так они выравниваются в колонки и читаются парами, а десятичные значения удобнее считать в уме.
Почему означает 10, а не букву n?
Потому что запись \n обозначает символ с десятичным номером 10, а это код перевода строки в таблице ASCII. Буква n рядом с десяткой стоит в старой системе счисления, которой пользовались до распространения двоичной записи, и это совпадение со временем закрепилось в синтаксисе языков программирования. Рядом живут и другие имена: \t это код 9, \r это код 13, \0 это код 0.
Можно ли записать кириллицу в ASCII?
Нет, в таблице просто нет ни одной русской буквы. Попытка записать её приведёт либо к ошибке, либо к тому, что на выходе получится совсем другой символ. Для русского текста нужна кодировка с кириллицей: Windows-1251, KOI8-R или, что предпочтительнее сегодня, UTF-8. Про разницу между Unicode и UTF-8 и про то, как не получить кракозябры, стоит почитать отдельно.
Почему в файле вместо текста непонятные символы?
Скорее всего, файл читается не в той кодировке, в какой записан. Определить кодировку по первым байтам и сравнить результаты чтения помогает детектор кодировки. Если же вместо текста видны настоящие непечатные символы, скорее всего, файл просто двоичный, и открывать его текстовым редактором не нужно.
ASCII Арт Текст
Конвертируйте текст в баннеры ASCII арт, используя шрифты в стиле FIGlet с 5 стилями, настраиваемой шириной и копированием
Двоичный переводчик
Конвертируйте текст в двоичный код (0 и 1) и обратно с побайтовым разбором
Конвертер систем счисления
Конвертируйте числа между двоичной, восьмеричной, десятичной, шестнадцатеричной и произвольной системами
Переводчик азбуки Морзе
Конвертируйте текст в азбуку Морзе и обратно с визуальным представлением и звуком