Если вы программист или хотя бы немного связаны с программированием1, то без сомнения сталкивались с таблицей ASCII.

Таблица ASCII полезна. Но знали ли вы, что она ещё красива и изящна?

Сегодня даже не близкие к программированию люди могут знать об ASCII благодаря книгам и фильмам наподобие «Марсианина»2

ASCII по-прежнему с нами; даже если вы передаёте современный Unicode3, то должны знать, что самый популярный формат кодировки UTF-8 специально спроектирован как обратно совместимый с ASCII! Декодировав эту статью как ASCII, вы в целом поймёте её смысл… если закроете глаза на мусорные символы в конце предложений (прим. ред.: имеет смысл только для оригинальной статьи на английском).

1. Связаны с программированием? Ага. Например, геокэшеры, которые хотя бы раз кодировали геокэш с координатами, заданными в двоичном виде (то есть в двоичном представлении ASCII) в данном случае считаются «связанными с программированием».

2. И в книге, и в фильме Марк Уотни разделяет круг рядом с восстановленной станцией Pathfinder на сегменты, соответствующие шестнадцатеричным цифрам от 0 до F, чтобы поворот камеры (выполняемый операторами с Земли) мог передавать пары 4-битных слов. Два 4-битных слова составляют 8-битный байт, который можно декодировать как ASCII, восстановив таким образом связь с Землёй.

3. Например, чтобы отправлять свои любимые эмодзи.


История


Изначально кодировка ASCII была стандартизирована в документе с легко запоминающимся названием X3.4-1963, где было присвоено значение сотне из потенциальных 128 кодовых точек, представляемых в двоичном 7-битном виде4, то есть двоичными значениями от 0000000 до 1111111:


Можно заметить, что в этой первой реализации отсутствует… весь алфавит в нижнем регистре! Есть тут и некоторые странности, на которые могут обратить внимание фанаты современного ASCII, например, любопытные стрелки влево и вправо внизу столбца 101____, а также управляющие коды ACK и ESC в столбце 111____.

4. ASCII многие считают 8-битным кодом, но на самом деле он 7-битный. Поэтому почти в каждом ASCII-сообщении практически все октеты начинаются с нуля. 8 битов удобны для передачи, но первые 8-битные системы чаще всего использовали восьмой бит для проверки чётности, чтобы выявлять ошибки передачи. Разумеется, при этом ничто не мешает передавать один за другим поток 7-битных символов.

Если вы уже догадались, к чему я веду, то вам должно быть интересно взглянуть на таблицу X3.4-1963 и убедиться в том, что многие из тех же изящных решений уже существовали в 1963 году. Это очень круто!

Таблица


На случай, если вы ещё незнакомы с таблицей, давайте рассмотрим её. Я отметил цветом некоторые её части, которые считаю самими красивыми:


В этой таблице показаны только десятеричные и шестнадцатеричные значения символов, но чтобы оценить её красоту, нам понадобятся и двоичные.

▍ Управляющие коды


Первые 32 «символа» (и, можно сказать, последний тоже) мы не можем увидеть, это команды, передаваемые между машинами. Возможно, вам известен «возврат каретки» (carriage return, 0D) и «перевод строки» (line feed, 0A), означающие «вернуться к началу этой строки» и «перейти на следующую строку»5.

5. Во времена, когда данные отправлялись телетайпным принтерам, эти два символа имели сильно отличающееся значение; иногда устройства так медленно возвращали головки в левую часть бумаги, что требовалось дополнительно отправлять несколько нулевых байтов, например 0D 0A 00 00 00 00, чтобы печатающая головка точно оказывалась в нужном месте до отправки новых данных: тогда у принтеров ещё не было буферов памяти! Ради совместимости с телетайпами в первых миникомпьютерах соблюдался тот же стандарт «возврат каретки плюс перевод строки» даже при выводе текста на экраны. В дальнейшем для сохранения обратной совместимости уже с этими системами следующее поколение компьютеров тоже для обозначения следующей строки использовало символ возврата каретки и перевода строки. Поэтому даже сегодня многие компьютерные системы (в том числе по большей мере Windows и многие протоколы Интернета) продолжают использовать комбинацию возврата каретки и символа перевода строки каждый раз, когда им нужно перейти на следующую строку; эта избыточность встроена в цепь обратной совместимости, устаревшей уже десятки лет назад, но оставшейся с нами навечно как часть нашего цифрового наследия.

Многие из прочих больше широко не используются, они были спроектированы для компьютерных систем, совершенно отличающихся от нынешних, но всё равно присутствуют в таблице.

Число 32 является степенью двойки, то есть можно логично предположить, что эти управляющие коды имеют в своём двоичном виде определённый математический «паттерн», отличающий их от остальной части таблицы. И это действительно так! Все управляющие коды соответствуют паттерну00_____, то есть начинаются с двух нулей. Поэтому когда мы считываем 7-битный элемент ASCII6, то если он начинается с 00, это непечатаемый символ. Все остальные символы печатаемы.

6. Получили 8 двоичных цифр? Вероятно, первая равна нулю. Отбрасываем её. Теперь мы получили 7-битный ASCII. Вот и всё.

Этот паттерн не только упрощает чтение кодировки людьми (таким образом повышая его упорядоченность и усиливая красоту), но и помогает, если вы работаете на древней медленной компьютерной системе, сравнивающей по одному биту информации за раз. В таком случае можно использовать дерево принятия решений для ускорения работы.


Среди управляющих кодов есть одно исключение: DEL, последний символ таблицы, соответствующий двоичному числу 1111111. Это привет из прошлого, когда работа велась с бумажными лентами: для обозначения нулей и единиц клавиатура пробивала комбинации из семи отверстий. Удалять отверстия после их пробивания было нельзя, поэтому единственным способом пометить символ как недействительный была перемотка ленты и пробивание всех отверстий в этой позиции, то есть всех единиц.

▍ Пробел


Первый печатаемый символ — это пробел; это невидимый символ, но он всё равно значим для людей, так что это не управляющий символ (сегодня это кажется очевидным, то при изначальном обсуждении стандарта ASCII это вызвало споры о семантике).

Помещение его численно перед всеми печатаемыми символами было очень продуманным и преднамеренным решением. Причина этого заключается в сортировке. Проще всего компьютеру отсортировать список (файлов, строк или чего-то ещё) численным способом, применив ту же таблицу преобразования символов, что и для всего другого7.

7. Я крайне благодарен разделу 13.8 книги Coded Character Sets, History and Development Чарльза Маккензи (1980 год), весь текст которой выложен бесплатно онлайн, за помощь в понимании важности позиции символа пробела в наборе символов ASCII. Основную часть изложенного в посте я уже знал давно, но раньше я не осознавал полностью важность расположения пробела!

Естественнее всего, чтобы символ пробела шёл перед всеми остальными символами, или же в отсортированном компьютером списке John Smith не будет идти перед Johnny Five, как мы того ожидаем.

Кроме того, что это первый печатаемый символ, пробел имеет красивый и запоминающийся двоичный вид, легко узнаваемый человеком: 0100000.

▍ Числа


Расположение арабских чисел 0-9 тоже не было совпадением. Их позиция означает, что они начинаются с нуля, находящегося в красивом круглом двоичном значении 0110000 (и аналогично круглом шестнадцатеричном значении 30) и продолжаются последовательно:


Последние четыре разряда двоичного числа описывают значение соответствующей десятеричной цифры. Кроме того, последний разряд шестнадцатеричного числа равен десятеричной цифре. Это просто потрясающе!

Если вы решили использовать этот пост, чтобы научиться мысленно «читать» ASCII в двоичном виде, то следует запомнить такое правило: если символ начинается с 011, то оставшуюся часть числа нужно воспринимать как двоичное представление самого числа. Вероятно, вы не ошибётесь: если получившееся число больше 9, то, скорее всего, это уже какой-то знак препинания.

▍ Числа с шифтом


Вычтите 0010000 из каждого числа, и вы получите числа с шифтом. К сожалению, первое уже занято символом пробела, но в случае всех остальных символы являются тем, что вы получаете при одновременном нажатии на клавишу Shift и числовую клавишу.

«Но ведь нет!», — воскликнете вы. Да, возможно, вы правы. Я печатаю на 105-клавишной QWERTY-клавиатуре ISO/UK и только четыре из девяти цифр 1-9 правильно отражены в таблице ASCII.

Боюсь, это вызвано тем, что ASCII создан на основе не современных компьютерных клавиатур, а позиций механической пишущей машинки Remington No. 2, раскладка со сдвигом которой, вероятно, в те времена была компромиссом, наиболее близким к стандарту. Но если вас это утешит, то можно сказать, что вы узнали что-то новое о пишущих машинках.


Любопытный факт в нагрузку: в первых механических пишущих машинках не было цифры 1: ожидалось, что вместо неё будет использоваться буква I. Для печати это нормально, но не особо удобно для считываемых компьютером данных.

▍ Буквы


Как и цифры, буквы имеют паттерн. После символа @ со значением 1000000 все буквы в верхнем регистре начинаются с 10, а далее идёт двоичное представление их позиции в алфавите. 1 = A = 1000001, 2 = B = 1000010 и так далее до 26 = Z = 1011010. Если вы сможете выучить числа позиций букв алфавита, то умеете считать в двоичном коде, и этого будет достаточно, чтобы прочитать любую букву верхнего регистра в ASCII, закодированную в двоичном виде8.

8. Я уверен, что вы уже это знаете, но на случай, если вы один из сегодняшних 10000 счастливчиков, скажу, что причина того, что мы называем заглавные и строчные буквы верхним (uppercase) и нижним (lowercase) регистром, связана с печатью в 19-м веке, когда наборный шрифт хранился в коробках (type case) в зависимости от типа символа. Обычно заглавные буквы хранились в верхнем (upper) ящике.

А разобравшись с верхним регистром, легко понять и нижний. Позиция этих символов в таблице означает, что они ровно на 0100000 выше, чем соответствующая буква в верхнем регистре; то есть все буквы в нижнем регистре начинаются с 11! 1 = a = 1100001, 2 = b = 1100010, а 26 = z = 1111010.

Возможно, вам непонятно, почему буквы в верхнем регистре идут первыми — причина снова в сортировке; кроме того, первая реализация ASCII, которую мы видели выше, была придумана ещё до того, как стало понятно, что компьютерным системам нужно будет разделять коды символов букв в верхнем и нижнем регистре (можно разработать альтернативную реализацию, которая бы вместо этого, например, передавала управляющие коды, сообщающие получателю о необходимости смены регистра). Учитывая современные технологии, я рад, что в конечном итоге разработчики стандарта выбрали правильное решение.

Красота


ASCII обладает странным неуловимым очарованием. Мы используем этот стандарт (и производные от него) буквально каждый день, поэтому легко подумать, что это просто какая-то произвольная кодировка.

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

Telegram-канал со скидками, розыгрышами призов и новостями IT ?

Комментарии (25)


  1. dlinyj
    24.07.2024 13:45
    +2

    Здесь не хватает описание что же такое ASCII (англ. American standard code for information interchange). А весь восторг прекрасно изложен в русской википедии ASCII

    Забавно, что когда много работаешь с ней, то на память уже знаешь многие символы. Например пробел 0x20, любое число - это число + 0x30, ESC 0xB1 или \033.


    1. Oangai
      24.07.2024 13:45

      мне когда-то приходилось много умножать в уме в шестнадцатеричной системе, когда в hex-кодах для 8080 программировал. Распечатка этой таблички перед глазами очень помагала, был определённый алгоритм в голове, чтобы не сбиваться


  1. inkelyad
    24.07.2024 13:45
    +3

    Меня огорчает, что кто-то в свое время забыл о существовании FILE/GROUP/UNIT/RECORD SEPARATOR и в результате родился уродец, который CSV. Ну и сейчас хорошо бы их применять во многих случаях, когда в строку отдельные части чего-нибудь пытаются уложить - вместо пробелов/запятых/двоеточий


    1. Oangai
      24.07.2024 13:45

      не то что бы кто-то их забыл, но наверное в 63м году проблематика структурирования данных как-то не стояла на повестке, изобретению COBOL на тот момент было всего 3 года как


      1. inkelyad
        24.07.2024 13:45

        но наверное в 63м году проблематика структурирования данных как-то не стояла на повестке, изобретению COBOL на тот момент было всего 3 года как

        Так я про позднее. Забыли давно и надежно. Вот какой стандартный (т.е. первый приходящий на ум) формат файла, когда нужно экспортировать несколько таблиц с небинарными данными?

        А оно как бы именно при помощи этих символов легко делается и потом столь же легко парсится.


        1. Oangai
          24.07.2024 13:45

          просто структурирование делалось с изобретения Коболя лет 20 только в двоичном виде, текстовые форматы вроде csv обрели популярность только с 80x, когда основные кодировки уже устоялись


          1. inkelyad
            24.07.2024 13:45

            Честно говоря, не понял аргумента.

            Мой, если развернуть, приблизительно так будет: Вот есть у нас, скажем, Excel-табличка. Миллиарды их в разных местах.

            Нужно ее экспортировать и прочитать чем-то, для чего лень либу чтения самого Excel искать или ее (на тот момент) просто не существовало. Во что эту табличку сохранят? Вот csv будет же с запятыми вместо прочих разделителей. С заметным повышением вероятности что эти данные где-то потом неправильно распарсят.


            1. Oangai
              24.07.2024 13:45
              +1

              я просто имею в виду про историческое развитие: оно сейчас не очевидно, но когда кодировки вводились, в то время и еще долго потом не стояло проблемы сериализации и структурирования текстовых данных. Проблема, которую вы имеете в виду, скоре в том что в 80х никто не удосужился выработать какой-то общий стандарт для такой сериализации. Ввели кавычки для полей, это понятно, но как тогда экранировать кавычку в самом поле, каким символом? А нужно ли экранировать CR/LF в поле? Стандарта нет, каждый разработчик фантазирует как может.

              Исторически, COBOL вообще первый язык который ввёл структуры данных, но они там долгое время все фиксированной длинны и не требовали дополнительной разметки при хранении и передачи данных - программа просто знала, что например первые 20 байт для имени, след. 20 для фамилии, один для пола, итд., так и работали.

              Поля переменной длинны хорошо позже возникли, это была целая смена парадигмы, как вообще с данными работать.

              Активная работа с текстами в нашем понимании зарождается где-то в академической среде с unix в семидесятых, к 80м достигает массового пользователя в виде текстовых редакторов и табличных процессоров, вот тогда csv и понадобился


              1. inkelyad
                24.07.2024 13:45

                Проблема, которую вы имеете в виду, скоре в том что в 80х никто не удосужился выработать какой-то общий стандарт для такой сериализации.

                Так я как раз про то, что на этот момент ASCII с ее сепараторами - была (вот, скажем, 1968 год), т.е. прямо в нее как раз уже были внесены кое-какие средства сериализации. Но ими не воспользовались, а начали использовать вот все эти кавычки и запятые.


                1. Oangai
                  24.07.2024 13:45
                  +1

                  может кто их и предложил по доброте душевной, но видимо в то время никто не понял зачем это нужно, перед ними другие задачи стояли.

                  Вообще, вплоть до 90х данными рулил в основном IBM с его майнфреймами, так он там вообще умудрялся спокойно жить с шестью несовместимыми вариантами своей EBCDIC, спал спокойно и совесть не мучила :) Помню, когда в начале 90х изучал Cobol, в руководствах для оператора указывалось что, при необходимости перекодировки в другой формат вроде ascii, для этого к мейнфрейму докупается специальная железка- перекодировщик, а сам он такой мелочью не занимается...


        1. vadimr
          24.07.2024 13:45

          Исторически всё хранилось в фиксированном формате, строго по позициям. Поэтому никакие разделители были не нужны. Просмотрите на ввод-вывод в Коболе и Фортране. Тратить ресурсы процессора на распознавание длины элемента было слишком дорого, просто тупо копировалось фиксированное количество байтов между устройством и памятью.


    1. N-Cube
      24.07.2024 13:45
      +1

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


      1. inkelyad
        24.07.2024 13:45

        Приблизительно все текстовые редакторы (кроме уж совсем-совсем тупых) умели и умеют работать с непечатными символами. Кроме того, файлы сериализации/импорта/экспорта, в общем, и не предполагаются для распечатки и лишь условно предполагаются для чтения. Когда кто-то csv делает - это не для того, чтобы читать и редактировать (это прямо в Excel делать хорошо), оно в подавляющем случае для того, чтобы в какую-то другую программу втянуть.

        как их посмотреть и вставить и чем они от пробелов отличаются.

        И тут эти сепараторы как раз хороши - потому что они на отображении отличаются.


        1. sotland
          24.07.2024 13:45

          Так какой символ считаете нужно было использовать вместо ,


          1. Oangai
            24.07.2024 13:45
            +2

            не то что вместо, человек скорее удивляется (вполне заслуженно) что специальные символы для этого даже одно время были предусмотрены, но потом их забыли как-то.


          1. inkelyad
            24.07.2024 13:45

            Так какой символ считаете нужно было использовать вместо ,

            Цитата из описания:

            FS (File Separator), GS (Group Separator), RS (Record Separator), and US (Unit Separator): These information separators may be used within data in optional fashion, except that their hierarchical relationship shall be: FS is the most inclusive, then GS, then RS, and US is least inclusive. (The content and length of a File, Group, Record, or Unit are not specified.)

            Вместо запятой, соответственно(которая разделитель столбцов таблицы) - Unit Separator.


            1. Oangai
              24.07.2024 13:45

              Кстати, вот: https://www.rfc-editor.org/rfc/rfc20 - вполне конкретно определяет назначение всех управляющих символов, 69-й год. Похоже что изобретателям csv не очень интересно было во всякие эти RFC заглядывать.

              С другой стороны, этот RFC ведь определяет не формат файла, а формат обмена по сети; для кого-то это могло быть достаточной причиной не захотеть его для файлов использовать.


  1. Oangai
    24.07.2024 13:45
    +1

    вообще, если кому интересно, предыстория сабжа:

    https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%B4_%D0%91%D0%BE%D0%B4%D0%BE

    https://ru.wikipedia.org/wiki/%D0%9C%D0%A2%D0%9A-2

    Телетайпы с МТK-2 я еще застал на своей первой работе, начало 90х, забавные были агрегаты


    1. vesowoma
      24.07.2024 13:45

      Интересно, что в МТК-2 цифра 4 вместо буквы Ч,

      а на ж/д буква Ч вместо цифры 4


  1. ImagineTables
    24.07.2024 13:45
    +2

    Любопытный факт в нагрузку: в первых механических пишущих машинках не было цифры 1: ожидалось, что вместо неё будет использоваться буква I. Для печати это нормально, но не особо удобно для считываемых компьютером данных.

    А что, идея хороша. Можно сделать такой ЯП, где из контекста должно выводиться, цифра у нас или буква:

    if (I0 == 1O) // Компилятор: "Не знаю, что вы имели в виду, но это точно true!"
    {
        I = 0;
        O = 1; // Happy debugging, bitches!
        0x = Ox; // Ошибка: не хватает значащих цифр! (Справа от =, если что).
    }
    


    1. Oangai
      24.07.2024 13:45
      +1

      вот, для развития идеи :)

      http://p-nand-q.com/programming/languages/java2k/manual.html


  1. Alikberov
    24.07.2024 13:45

    Как-то размышлял на тему и даже пытался работать над темой ("перезагрузить ASCII" в рамках пилотной архитектуры) и могу сказать, что ИМХО, в таблице ASCII имеется ряд недостатков:
    Первое: 32 управляющего кода - слишком много. Сейчас это - "мёртвый груз", так как большинство кодов безнадёжно морально устарело. Остаются популярными и достаточными лишь 00, 0D и 1B.
    Второе: Недостаточная "интуитивность" кодировки (особенно, для начинающих программистов). Почему цифра "1" и знак "!" располагаются на одной линии, а цифра "7" и знак "?" - нет, хотя они графически похожи, как "5" и "$", как "8" и "&"?
    (То есть, вместо «!"#$%&'()» "интуитивнее" было бы «!%#^$(?&)»: Делал как-то подобную раскладку у себя в системе. Быстро переучиваешься, привыкаешь.)


  1. matwel
    24.07.2024 13:45

    Раз уж пошла такая "пьянка", выскажу и я своё мнение (понятно что очень спорное) не о коде символов, а о клавише "Caps Lock" (о клавишах пишущей машинки в статье речь шла, однако).

    Когда у меня запущено/открыто несколько программ или несколько документов, бывает надо периодически переходить то на одну/один, то на другую/другой. Понятно, что один документ я набираю на русском языке, другой скажем на английском, и переходя между документами у меня нет необходимости опять выбирать раскладку. Раскладка меняется автоматически.

    Но до чего же выбешивает иногда общий для всех документов/программ "Caps Lock". Неужели сейчас нет возможности привязать его состояние для каждого документа/программы индивидуально, что бы не приходилось потом править фрагмент текста с ошибочным регистром, установленным в предыдущем документе/программе



    1. vadimr
      24.07.2024 13:45

      Тут такое дело. Раскладка – это просто программный флажок, а Caps Lock – аппаратный триггер в клавиатуре. Он ничего не знает об окнах. Можно, конечно, его переключать со стороны компьютера, но тогда пойдёт рассинхронизация при сбоях связи, особенно на блютузе. Буквы при перебоях со связью просто в буфере копятся, а с этим так не выйдет.


  1. GBR-613
    24.07.2024 13:45
    +2

    Все познаётся в сравнении. Чтобы понять величие ASCII, сравните её с EBCDIC.