14 KiB
Кодирование символов *
В компьютере все данные хранятся в виде двоичных чисел, и символы char не являются исключением. Для представления символов необходимо создать «набор символов», определяющий взаимно однозначное соответствие между каждым символом и двоичным числом. Имея набор символов, компьютер может выполнять преобразование двоичных чисел в символы путем поиска в таблице.
Набор символов ASCII
Код ASCII является самым ранним набором символов, его полное название — American Standard Code for Information Interchange (Американский стандартный код для обмена информацией). Он использует 7-битные двоичные числа (младшие 7 бит одного байта) для представления символа и может представлять максимум 128 различных символов. Как показано на рисунке ниже, код ASCII включает прописные и строчные буквы английского алфавита, цифры 0~9, некоторые знаки препинания, а также управляющие символы (такие как символ новой строки и табуляции).
Однако код 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 байта и восстановить содержание этой фразы.
Однако код 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, распространенные методы кодирования включают следующие два.
- Кодирование UTF-16: использует 2 или 4 байта для представления символа. Все символы ASCII и часто используемые неанглийские символы представлены 2 байтами; небольшое количество символов требует 4 байта. Для 2-байтовых символов кодирование UTF-16 равно кодовой точке Unicode.
- Кодирование UTF-32: каждый символ использует 4 байта. Это означает, что UTF-32 занимает больше места, чем UTF-8 и UTF-16, особенно для текстов с высокой долей символов ASCII.
С точки зрения занимаемого пространства хранения использование UTF-8 для представления английских символов очень эффективно, поскольку требуется только 1 байт;


