Представление текстовой информации. Кодировки.

Представление текстовой информации

Мы уже разобрались с кодированием целых и действительных чисел – и те и другие представляются в виде двоичного кода. Странно было бы ожидать чего-то другого от текстовой информации.

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

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

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

Кодировки могут иметь различную фиксированную битность, т.е. отводить заведомо известное количество бит под кодирование одного символа, а могут иметь переменную битность.

Кодировка ASCII

American standard code for information interchange – американский стандартный код обмена информацией.

Разработана в 1963 году, и в некоторой форме используется до сих пор.

ASCII является, по факту, семибитной кодировкой, однако сейчас каждый символ хранится в 8 битах, и старший бит всегда установлен в 0.

Всего в ASCII представимо, как можно догадаться, 128 символов. В их число входят: 32 управляющих символа, цифры, знаки препинания и математические символы, заглавные и строчные буквы латинского алфавита, арабские цифры.

Кодировка ASCII
0 1 2 3 4 5 6 7 8 9 a b c d e f
0
1
2 ! " # $ % & ( ) * + , - . /
3 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
4 @ A B C D E F G H I J K L M N O
5 P Q R S T U V W X Y Z [ \ ] ^ _
6 ` a b c d e f g h i j k l m n o
7 p q r s t u v w x y z { } ~ ␡

В настоящее время многие из управляющих символов используются редко. Наиболее часто используемые:

  • 0, 016, NUL, \0 – нулевой символ, используется во многих языках программирования как маркер конца строки.
  • 4, 416, EOT – “конец передачи”. Используется в некоторых системах (в частности Unix-совместимых) как маркер окончания ввода.
  • 7, 716, BEL, \a – получившее устройство передает пользователю сигнал (обычно писк)
  • 8, 816, BS, \b – используется либо для удаления последнего символа, либо для печати “поверх” него.
  • 9, 916, HT, \t – горизонтальная табуляция, используется для выравнивания строк
  • 10, A16, LF, \n – “перенос строки”, маркер конца строки в вариантах Unix
  • 11, B16, VT, \v – вертикальная табуляция
  • 12, C16, FF, \f – очищает экран терминала или, при печати, текущую страницу
  • 13, D16, CR, \r – “возврат каретки” – перемещает курсор в начало строки. Используется как маркер конца строки в Mac OS. \r\n используется как маркер конца строки в Windows.
  • 27, 1B16, ESC – “эскейп”, используется для введения команд управления терминалом.
  • 127, 7F16, DEL – в некоторых системах используется для удаления символа.

Национальные восьмибитные кодировки

Использование 8 бит для хранения 7-битных ASCII-символов позволяет дополнять кодировку ASCII символами национальных алфавитов, устанавливая старший бит в 1.

Для русского языка широкое распространение получили следующие кодировки:

  • CP866 – широко использовалась в MS-DOS
  • KOI8-R – широко использовалась в Unix-подобных ОС (Linux, BSD)
  • CP1251 – широко использовалась (и отчасти используется до сих пор) ОС семейства Windows.

Описательно, CP866, кроме русских букв, содержит псевдографические символы, KOI8-R построена таким образом, чтобы на терминалах, поддерживающих только семибитный ASCII русский текст превращался в транслит с инвертированным регистром (большие буквы становились маленькими, а маленькие – большими), а CP1251 содержит практически все типографские символы и, кроме русских, включает украинские и белорусские символы.

Кодировка CP866
0 1 2 3 4 5 6 7 8 9 A B C D E F
8 А Б В Г Д Е Ж З И Й К Л М Н О П
9 Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я
A а б в г д е ж з и й к л м н о п
B
C
D
E р с т у ф х ц ч ш щ ъ ы ь э ю я
F Ё ё Є є Ї ї Ў ў ° · ¤
Кодировка KOI8-R
0 1 2 3 4 5 6 7 8 9 A B C D E F
8
9 ° ² · ÷
A ё
B Ё ©
C ю а б ц д е ф г х и й к л м н о
D п я р с т у ж в ь ы з ш э щ ч ъ
E Ю А Б Ц Д Е Ф Г Х И Й К Л М Н О
F П Я Р С Т У Ж В Ь Ы З Ш Э Щ Ч Ъ
Кодировка CP1251
0 1 2 3 4 5 6 7 8 9 A B C D E F
8 Ђ Ѓ ѓ Љ Њ Ќ Ћ Џ
9 ђ љ њ ќ ћ џ
A Ў ў Ј ¤ Ґ ¦ § Ё © Є « ¬ ­ ® Ї
B ° ± І і ґ µ · ё є » ј Ѕ ѕ ї
C А Б В Г Д Е Ж З И Й К Л М Н О П
D Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я
E а б в г д е ж з и й к л м н о п
F р с т у ф х ц ч ш щ ъ ы ь э ю я

Кодировки UCS

В начале 1990-х, когда стало окончательно ясно, что многообразие восьмибитных кодировок создает больше проблем, чем решает, была создана универсальная кодировка UCS (ISO 10646), которая призвана включать все существующие символы, включая математические, символы мертвых и придуманных языков, идеограммы (смайлики и т.п.), и предполагала кодирование символов 32 битами.

Примерно одновременно с UCS появился стандарт Unicode, использовавший 16 бит для кодирования символов.

В результате дальнейшей эволюции, UCS и Unicode стали разрабатываться совместно. На текущий момент, существует три распространенных кодировки:

  • UTF-32
  • UTF-16
  • UTF-8

Поддержка UTF-32 реализована далеко не везде. Несмотря на то, что используются 32 бита, фактически значащими являются младшие 31. Теоретический максимум – 2 147 483 647 символов. Практически текущий стандарт допускает всего 1 114 111 различных кодов, хотя в будущем этот диапазон может быть расширен. UTF-32 – кодировка фиксированной длины.

UTF-16 реализует поддержку всех основных символов современных языков, плюс множество специальных символов, таких как математические символы, управляющие последовательности, диакритические знаки и т.п. Символ кодируется одним или двумя “словами” по 16 бит. Всего возможно 1 112 064 различных кодов. В старых реализациях, работают только коды из одного “слова”.

UTF-8, кодируется одним или несколькими октетами (по 8 бит), и является обратно-совместимой с ASCII. На текущий момент UTF-8 является наиболее популярной универсальной кодировкой: подавляющее большинство Unix-подобных ОС и подавляющее большинство страниц Веба используют именно ее.

Кодировка UTF-8 как кодировка переменной длины

В UTF-8 символы кодируются следующим образом:

  1. Если кодируется ASCII-символ, то старший бит устанавливается в 0, а остальные 7 бит представляют код ASCII-символа.
  2. Иначе, первые 8 бит содержат n единиц, где n – количество байт в кодируемом символе (то есть, минимум 2 единицы), за которыми следует 0. Остальные биты представляют часть кода символа.
  3. Следующие блоки по 8 бит начинаются на 10 (старший бит установлен в 1, следующий – в 0), а остальные биты представляют часть кода символа.

Максимальная длина кода символа в UTF-8 – 6 байт. Минимальная – 1 байт.

Пример:

(1 байт)  0aaa aaaa
(2 байта) 110x xxxx 10xx xxxx
(3 байта) 1110 xxxx 10xx xxxx 10xx xxxx
(4 байта) 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
(5 байт)  1111 10xx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx
(6 байт)  1111 110x 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx

Таким образом, технически UTF-8 может кодировать любую последовательность из UTF-32.

Следует заметить, что текущая версия стандарта Unicode 6 предполагает максимальный код 10FFFF16, для кодирования которого в UTF-8 достаточно 4 байт.

Теоретически, возможно кодирование более 31 бита в UTF-8, однако сейчас это не используется. Тем не менее, если первые 8 бит установлены в 1, то следующие 8 бит тоже могут быть интерпретированы как количество байт в коде.

Например:

(8 байт)  1111 1111 100x xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx (41 значащий бит)

(9 байт)  1111 1111 1010 xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx (46 значащих бит)

Примеры кодирования:

UCS Код UCS (двоичный) Символ Код UTF-8 Код UTF-8 (двоичный) Значащие биты UTF-8
007316 0000 0000 0111 0011 Латинская s 7316 0111 0011 1110011
041A16 0000 0100 0001 1010 Киррилическая К D09A16 1101 0000 1001 1010 10000 011010
090416 0000 1001 0000 0100 Индийская ऄ (“А”) E0A48416 1110 0000 1010 0100 1000 0100 0000 100100 000100
9DDF16 1001 1101 1101 1111 鷟 (“феникс”) E9B79F16 1110 1001 1011 0111 1001 1111 1001 110111 011111
1033016 0001 0000 0011 0011 0000 Готическая 𐌰 (“Ахса”) F0908CB016 1111 0000 1001 0000 1000 1100 1011 0000 000 010000 001100 110000
1F60416 0001 1111 0110 0000 0100 Смайлик 😄 F09F988416 1111 0000 1001 1111 1001 1000 1000 0100 000 011111 011000 000100

Пример реализации алгоритмов кодирования и декодирования на С++:

const unsigned char mask_low_6 = 0x3F; //0b00111111
const unsigned char mask_high_1 = 0x80; //0b10000000
const unsigned char bits_per_byte = 8;

std::string utf8enc(unsigned int codepoint) {
  if(codepoint<mask_high_1)
    return std::string(1,char(codepoint));
  //Ищем старший бит
  unsigned char bit = sizeof(unsigned int) * bits_per_byte - 1;
  while(!(codepoint & (1<<bit))) {
    bit--;
  }
  //Определяем количество байт, необходимых для кодирования
  unsigned char bytes = (bit+4)/5;
  std::string result(bytes,0);
  //Записываем само число
  for(unsigned char i=0; i<bytes-1; ++i)
    result[bytes-i-1] = mask_high_1 | ((codepoint>>i*6) & mask_low_6);
  //Записываем количество байт и "хвост"
  unsigned char len_mask = (~0)<<(bits_per_byte-bytes);
  unsigned char tail_mask = ~((~0)<<(bits_per_byte-bytes-1));
  result[0] = len_mask | ((codepoint>>(bytes-1) * 6) & tail_mask);
  return result;
}

unsigned int utf8dec(std::string r) {
  if(!(r[0] & mask_high_1))
    return r[0];
  //Считаем количество байт
  unsigned char bytes=0;
  while(r[0] & (mask_high_1>>bytes)) {
    bytes++;
  }
  //Выделяем "хвост" первого байта
  unsigned char tail_mask = ~((~0)<<(bits_per_byte-bytes-1));
  unsigned int codepoint = r[0] & tail_mask;

  //Считываем остальные
  for(unsigned char i=1; i<bytes; ++i) {
    codepoint = (codepoint<<6) | (r[i] & 0x3F);
  }
  return codepoint;
}

Кодировка UTF-16 как кодировка переменной длины

  1. Если слово не начинается на 110112, то это код из одного слова
  2. Иначе, это код из двух слов, причем первое слово начинается на 1101102, а второе – 1101112.
  3. Остальные биты – значащие. В случае кода из двух слов, к значащим битам прибавляется 1000016 для получения кода.

Диапазон UCS-кодов D80016..DFFF16 используется для кодирования так называемых суррогатных пар — символов, которые кодируются двумя 16-битными словами.

UCS-коды до FFFF16 кодируются двумя байтами, UCS-коды начиная с 1000016 – четырьмя.

Примеры:

UCS Код UCS (двоичный) Символ Код UTF-16 Код UTF-16 (двоичный)
007316 0000 0000 0111 0011 Латинская s 007316 0000 0000 0111 0011
041A16 0000 0100 0001 1010 Киррилическая К 041A16 0000 0100 0001 1010
090416 0000 1001 0000 0100 Индийская ऄ (“А”) 090416 0000 1001 0000 0100
9DDF16 1001 1101 1101 1111 鷟 (“феникс”) 9DDF16 1001 1101 1101 1111
1033016 0001 0000 0011 0011 0000 Готическая 𐌰 (“Ахса”) D800DF3016 1101 1000 0000 0000 1101 1111 0011 0000
1F60416 0001 1111 0110 0000 0100 Смайлик 😄 D83DDE0416 1101 1000 0011 1101 1101 1110 0000 0100