```markdown # Кодирование символов * В компьютере все данные хранятся в виде двоичных чисел, и символы `char` не являются исключением. Для представления символов необходимо создать «набор символов», определяющий взаимно однозначное соответствие между каждым символом и двоичным числом. Имея набор символов, компьютер может выполнять преобразование двоичных чисел в символы путем поиска в таблице. ## Набор символов ASCII Код ASCII является самым ранним набором символов, его полное название — American Standard Code for Information Interchange (Американский стандартный код для обмена информацией). Он использует 7-битные двоичные числа (младшие 7 бит одного байта) для представления символа и может представлять максимум 128 различных символов. Как показано на рисунке ниже, код ASCII включает прописные и строчные буквы английского алфавита, цифры 0~9, некоторые знаки препинания, а также управляющие символы (такие как символ новой строки и табуляции). ![Код ASCII](../assets/ascii_table.png) Однако **код ASCII может представлять только английский язык**. С глобализацией компьютеров появился набор символов EASCII, способный представлять больше языков. Он расширил 7-битный ASCII до 8 бит и может представлять 256 различных символов. По всему миру последовательно появились различные наборы символов EASCII, подходящие для разных регионов. Первые 128 символов этих наборов унифицированы как код ASCII, а последние 128 символов определены по-разному для удовлетворения потребностей различных языков. ## Набор символов GBK Позже люди обнаружили, что **код EASCII все еще не может удовлетворить требования к количеству символов во многих языках**. Например, существует почти сто тысяч китайских иероглифов, из которых несколько тысяч используются в повседневной жизни. В 1980 году Главное управление стандартизации Китая выпустило набор символов GB2312, который включал 6763 китайских иероглифа и в основном удовлетворял потребности компьютерной обработки китайских иероглифов. Однако GB2312 не мог обрабатывать некоторые редкие иероглифы и традиционные китайские символы. Набор символов GBK был получен путем расширения GB2312 и включает в общей сложности 21886 китайских иероглифов. В схеме кодирования GBK символы ASCII представлены одним байтом, а китайские иероглифы — двумя байтами. ## Набор символов Unicode С бурным развитием компьютерных технологий наборы символов и стандарты кодирования расцвели пышным цветом, что привело ко многим проблемам. С одной стороны, эти наборы символов обычно определяли только символы конкретного языка и не могли нормально работать в многоязычной среде. С другой стороны, для одного и того же языка существовало несколько стандартов наборов символов, и если два компьютера использовали разные стандарты кодирования, при передаче информации возникала кракозябра. Исследователи того времени думали: **если создать достаточно полный набор символов, включающий все языки и символы со всего мира, разве это не решит проблемы многоязычной среды и кракозябры**? Под влиянием этой идеи появился большой и всеобъемлющий набор символов Unicode. Unicode на китайском языке называется «统一码» (унифицированный код), теоретически может вместить более 1 миллиона символов. Он стремится включить символы со всего мира в единый набор символов, предоставляя универсальный набор символов для обработки и отображения текстов на различных языках, уменьшая проблемы с кракозяброй, возникающие из-за различий в стандартах кодирования. С момента выпуска в 1991 году Unicode постоянно пополняется новыми языками и символами. По состоянию на сентябрь 2022 года Unicode уже включает 149186 символов, включая символы различных языков, знаки и даже эмодзи. В огромном наборе символов Unicode часто используемые символы занимают 2 байта, а некоторые редкие символы занимают 3 или даже 4 байта. Unicode является универсальным набором символов, по сути присваивающим каждому символу номер (называемый «кодовой точкой»), **но он не определяет, как эти кодовые точки символов должны храниться в компьютере**. Мы не можем не задаться вопросом: когда кодовые точки Unicode различной длины одновременно появляются в тексте, как система анализирует символы? Например, при заданном коде длиной 2 байта, как система определяет, является ли это одним 2-байтовым символом или двумя 1-байтовыми символами? Для вышеуказанных проблем **прямым решением является хранение всех символов в виде кодов одинаковой длины**. Как показано на рисунке ниже, каждый символ в «Hello» занимает 1 байт, каждый символ в «算法» занимает 2 байта. Мы можем закодировать все символы в «Hello 算法» длиной 2 байта, заполнив старшие биты нулями. Таким образом, система может анализировать один символ каждые 2 байта и восстановить содержание этой фразы. ![Пример кодирования Unicode](../assets/unicode_hello_algo.png) Однако код ASCII уже доказал нам, что для кодирования английского языка требуется только 1 байт. Если использовать вышеуказанную схему, размер английского текста будет в два раза больше, чем при кодировании ASCII, что очень расточительно с точки зрения памяти. Поэтому нам нужен более эффективный метод кодирования Unicode. ## Кодирование UTF-8 В настоящее время UTF-8 стал наиболее широко используемым методом кодирования Unicode в мире. **Это кодирование переменной длины**, использующее от 1 до 4 байтов для представления символа в зависимости от сложности символа. Символы ASCII требуют только 1 байт, латинские и греческие буквы требуют 2 байта, часто используемые китайские иероглифы требуют 3 байта, а некоторые другие редкие символы требуют 4 байта. Правила кодирования UTF-8 не сложны и делятся на следующие два случая. - Для символов длиной 1 байт старший бит устанавливается в $0$, остальные 7 бит устанавливаются в кодовую точку Unicode. Стоит отметить, что символы ASCII занимают первые 128 кодовых точек в наборе символов Unicode. Это означает, что **кодирование UTF-8 обратно совместимо с кодом ASCII**. Это означает, что мы можем использовать UTF-8 для анализа старых текстов в кодировке ASCII. - Для символов длиной $n$ байт (где $n > 1$) старшие $n$ бит первого байта устанавливаются в $1$, бит $n + 1$ устанавливается в $0$; начиная со второго байта, старшие 2 бита каждого байта устанавливаются в $10$; все остальные биты используются для заполнения кодовой точки Unicode символа. На рисунке ниже показано кодирование UTF-8, соответствующее «Hello算法». Наблюдая, можно обнаружить, что поскольку старшие $n$ бит все установлены в $1$, система может определить длину символа $n$, прочитав количество единиц в старших битах. Но почему старшие 2 бита всех остальных байтов устанавливаются в $10$? На самом деле, это $10$ может служить контрольным символом. Предположим, система начинает анализировать текст с неправильного байта, $10$ в начале байта может помочь системе быстро обнаружить аномалию. Причина использования $10$ в качестве контрольного символа заключается в том, что согласно правилам кодирования UTF-8 невозможно, чтобы старшие два бита символа были $10$. Этот вывод можно доказать методом от противного: предположим, что старшие два бита символа равны $10$, это означает, что длина символа равна $1$, что соответствует коду ASCII. Но старший бит кода ASCII должен быть $0$, что противоречит предположению. ![Пример кодирования UTF-8](../assets/utf-8_hello_algo.png) Помимо UTF-8, распространенные методы кодирования включают следующие два. - **Кодирование UTF-16**: использует 2 или 4 байта для представления символа. Все символы ASCII и часто используемые неанглийские символы представлены 2 байтами; небольшое количество символов требует 4 байта. Для 2-байтовых символов кодирование UTF-16 равно кодовой точке Unicode. - **Кодирование UTF-32**: каждый символ использует 4 байта. Это означает, что UTF-32 занимает больше места, чем UTF-8 и UTF-16, особенно для текстов с высокой долей символов ASCII. С точки зрения занимаемого пространства хранения использование UTF-8 для представления английских символов очень эффективно, поскольку требуется только 1 байт; ```