В информатике письменность — это не просто способ зафиксировать речь на бумаге. Это формальная система представления информации с помощью знаков, подчиняющаяся строгим правилам синтаксиса и семантики. В отличие от естественного языка, где допускается двусмысленность, здесь каждый символ имеет однозначное значение.
Ключевая задача — обеспечить надежное хранение, передачу и обработку данных машиной. Любая программа, файл конфигурации или сетевой пакет — это текст, записанный по правилам некоторой искусственной письменности. Без единых стандартов кодировки (ASCII, Unicode) обмен данными между разными системами был бы невозможен.
Исторический контекст: от кода Морзе до Unicode
Первой «цифровой» письменностью стал телеграфный алфавит Сэмюэля Морзе (1838). Он использовал всего два знака — точку и тире — для кодирования букв и цифр. Это был прообраз двоичного кодирования, лежащего в основе современных компьютеров.
В 1963 году стандарт ASCII (American Standard Code for Information Interchange) унифицировал представление 128 символов 7-битовыми кодами. Но он охватывал только латиницу. Решением стало появление Unicode в 1991 году — универсального стандарта, присваивающего уникальный номер (code point) каждому знаку почти всех письменностей мира.
Алфавит, строка и язык: формальные основы
В теории формальных языков алфавит — это конечное непустое множество символов. Строка (слово) — конечная последовательность символов из алфавита. Язык — множество строк, удовлетворяющих заданным правилам (грамматике).
Например, алфавит {0, 1} порождает язык всех двоичных строк. А язык программирования Python — это множество строк, корректных с точки зрения его грамматики. Компилятор или интерпретатор проверяет принадлежность введённого текста к языку.
⚠️ Внимание: не путайте алфавит как множество символов с набором глифов шрифта. Один и тот же символ (code point) может отображаться разными глифами в зависимости от шрифта и локали.
Системы счисления как вид письменности
Позиционные системы счисления — это специализированные письменности для представления чисел. Двоичная (база 2) используется процессорами, шестнадцатеричная (база 16) — программистами для компактной записи байтов, восьмеричная (база 8) — в правах доступа Unix.
Запись числа 255 в разных системах: 11111111₂, 377₈, FF₁₆. Это один и тот же объект, но разные строковые представления. Выбор системы зависит от контекста: человек читает hex, машина работает с битами.
☑️ Проверка понимания систем счисления
Кодировки символов: от байта к кодовой точке
Стандарт Unicode определяет code point (кодовую точку) — число, присвоенное символу. Но для хранения в памяти нужна кодировка — правило превращения кодовых точек в последовательность байтов.
Самая распространённая — UTF-8. Она использует 1–4 байта на символ: латиница занимает 1 байт, кириллица — 2, эмодзи — 4. Это обеспечивает обратную совместимость с ASCII. UTF-16 (2 или 4 байта) применяется внутри Java, JavaScript, Windows API. UTF-32 (всегда 4 байта) удобен для случайного доступа, но расточителен по памяти.
| Кодировка | Размер символа | ASCII-совместима | Где применяется |
|---|---|---|---|
| UTF-8 | 1–4 байта | Да | Веб, Linux, БД, JSON |
| UTF-16 | 2 или 4 байта | Нет | Java, JS, Windows, Qt |
| UTF-32 | 4 байта | Нет | Внутренние буферы, ICU |
| Windows-1251 | 1 байт | Нет | Легаси Windows (кириллица) |
Почему UTF-8 стал стандартом де-факто?
UTF-8 изобрёл Кен Томпсон за ночь в 1992 году. Гениальность в том, что байты 0x00–0x7F кодируются сами собой (как в ASCII), а старшие биты служат маркерами длины последовательности. Это позволяет искать подстроки байтовым сравнением, не декодируя весь текст. Также UTF-8 не имеет проблем с порядком байтов (endianness), в отличие от UTF-16/32.
Разметка и структурированные данные
Языки разметки (Markup Languages) — это письменности для описания структуры документа. HTML задаёт семантику веб-страницы, XML — произвольные древовидные данные, Markdown — упрощённый формат для чтения человеком.
Сериализационные форматы JSON, YAML, TOML — тоже письменности, но ориентированные на структуры данных (объекты, массивы, скаляры). Они имеют строгий синтаксис: пропущенная запятая в JSON делает весь документ невалидным.
Пример JSON-объекта:
{
"encoding": "UTF-8",
"version": 2,
"enabled": true
}
⚠️ Внимание: никогда не парсьте HTML регулярными выражениями. HTML — нерегулярный язык (в смысле теории Хомского), а regex работает только с регулярными. Используйте полноценные парсеры:BeautifulSoup,lxml,DOMParser.
В JSON ключи всегда в двойных кавычках. Одинарные кавычки или их отсутствие — синтаксическая ошибка. Используйте валидаторы (jsonlint.com) перед отправкой в API.
Программные языки: письменность для исполнения
Языки программирования — это формальные письменности с операционной семантикой. Текст программы не просто хранит информацию, он предписывает действия исполнителю (процессору, виртуальной машине, интерпретатору).
Лексический анализатор (лексер) разбивает исходный код на токены — ключевые слова, идентификаторы, литералы, операторы. Синтаксический анализатор (парсер) строит АСТ (абстрактное синтаксическое дерево) по грамматике языка. Дальше — семантический анализ, оптимизация, генерация кода.
Пример токенизации строки x = 42 в Python:
NAME 'x'
EQUAL '='
NUMBER '42'
NEWLINE '\n'
Любая программа — это текст на формальном языке. Качество кода (читаемость, отсутствие багов) напрямую зависит от понимания программистом правил этой письменности: лексики, синтаксиса, семантики.
Проблемы интернационализации (i18n)
Современная письменность в IT должна поддерживать мультиязычность. Это не только Unicode. Нужно учитывать: направление письма (LTR/RTL для арабского, иврита), лигатуры (сочетание глифов), нормализацию (NFC/NFD — разные способы представления одного визуального символа), сортировку (collation) по правилам локали.
Пример: символ é может быть одной кодовой точкой U+00E9 (NFC) или двумя — e + U+0301 (NFD). Визуально одинаковы, но бинарно разные. Поиск и сравнение строк требуют предварительной нормализации.
⚠️ Внимание: функция.toLowerCase()в JavaScript работает некорректно для некоторых локалей (например, турецкого «İ» → «i» с точкой). Всегда используйте.toLocaleLowerCase('tr')с явным указанием локали.
Что такое графема и почему length лжёт?
В Unicode «символ» (code point) ≠ «визуальный символ» (графема). Эмодзи 👨👩👧👦 — это 5 кодовых точек (U+1F468 U+200D U+1F469 U+200D U+1F467 U+200D U+1F466), склеенных Zero Width Joiner. В JS "👨👩👧👦".length === 11. Для работы с графемами нужен Intl.Segmenter или библиотека grapheme-splitter.
Будущее: бинарные форматы и эффективность
Текстовые форматы (JSON, XML, YAML) читаемы людьми, но избыточны и медленны в парсинге. Для высоконагруженных систем используют бинарные письменности: Protocol Buffers (protobuf), MessagePack, CBOR, Avro, FlatBuffers.
Они кодируют схему данных отдельно от полезной нагрузки. Поле «user_id: 12345» в JSON занимает ~15 байт, в protobuf — 2–3 байта (varint). Парсинг в 10–100 раз быстрее. Но читать такой файл в текстовом редакторе нельзя — нужен десериализатор со схемой.
Выбор между текстовым и бинарным форматом — компромисс между наблюдаемостью (debugging, логи) и производительностью (пропускная способность, задержка). Микросервисы часто используют gRPC + protobuf внутри кластера и JSON/REST на границе с внешним миром.
☑️ Выбор формата сериализации
FAQ: частые вопросы о письменности в IT
Почему в базе данных стоит utf8mb4, а не utf8?
В MySQL старый псевдоним utf8 на самом деле означает utf8mb3 — только 3-байтовые символы (BMP). Эмодзи и редкие иероглифы требуют 4 байта. utf8mb4 — полноценный UTF-8. Всегда используйте utf8mb4 и соответствующие коллации (utf8mb4_unicode_ci).
Чем кодовая страница отличается от кодировки?
Кодовая страница (code page) — термин Microsoft/IBM для однобайтовых кодировок (CP1251, CP866, CP437), где каждому байту 0x00–0xFF ставится в соответствие символ. Кодировка — общее понятие правила отображения кодовых точек в байты, включая многобайтовые (UTF-8, UTF-16).
Зачем нужна BOM (Byte Order Mark)?
BOM (U+FEFF) в начале файла указывает на кодировку и порядок байтов (endianness) для UTF-16/UTF-32. В UTF-8 BOM не нужен (порядок байтов фиксирован), но иногда добавляют для автоопределения. В вебе BOM в UTF-8 вреден — ломает HTTP-заголовки и JSON-парсеры.
Почему регулярные выражения не подходят для парсинга HTML/XML?
HTML/XML — контекстно-свободные языки (Type-2 в иерархии Хомского). Регулярные выражения описывают только регулярные языки (Type-3). Вложенные теги произвольной глубины требуют стека — возможности, которой нет у regex. Используйте DOM-парсеры или SAX/StAX для потокового чтения.
Что такое нормализация Unicode и когда она нужна?
Нормализация приводит эквивалентные последовательности кодовых точек к единому виду. NFC — композиция (один символ, где возможно), NFD — декомпозиция (база + диакритики). Нужна перед сравнением строк, поиском, индексацией в БД, генерацией хэшей. В Python: unicodedata.normalize('NFC', s).