Кодирование текста. Кодирование символов. Unicode
Этот пост для тех, кто не понимает, что такое UTF-8, но хочет это понять, а доступная документация часто очень обширно освещает этот вопрос. Я попробую здесь описать это так, как сам бы хотел, чтобы раньше мне кто-то так рассказал. Так как часто у меня по поводу UTF-8 была в голове каша.
Несколько простых правил
- Итак, UTF-8 — это «обертка» для Unicode. Это не отдельная кодировка символов, это «обертнутый» Unicode. Вы, наверное, знаете Base64 кодировку, или слышали о ней — она может обернуть бинарные данные в печатаемые символы. Дак вот, UTF-8 это такой же Base64 для Unicode, как Base64 для бинарных данных. Это раз. Если вы это поймете, то уже многое станет ясно. И она также, как Base64, признана решить проблему совместимости в символах (Base64 была придумана для email, чтобы передавать файлы почтой, в которой все символы — печатаемые)
- Далее, если код работает с UTF-8, то внутри он все равно работает с Unicode кодировками, то есть, где-то глубоко внутри есть таблицы символов именно Unicode символов. Правда, можно не иметь таблиц символов Unicode, если надо просто посчитать, сколько символов в строке, например (см. ниже)
- UTF-8 сделан с той целью, чтобы старые программы и сегодняшние компьютеры могли работать нормально с Unicode символами, как со старыми кодировками, типа KOI8, Windows-1251 и т.п.. В UTF-8 нет байтов с нулями, все байты — они либо от 0x01 — 0x7F, как обычный ASCII, либо 0x80 — 0xFF, что также работает под программами, написанными на Си, как и работало бы не с ASCII символами. Правда, для корректной работы с символами программа должна знать Unicode таблицы.
- Все, что имеет старший 7-ой бит в байте (если считать биты с нулевого) UTF-8 — часть кодированного потока Unicode.
UTF-8 изнутри
Если вы знаете битовую систему, то вот вам краткая памятка , как кодируется UTF-8:
Первый байт Unicode символа в UTF-8 начинается с байта, где 7-ой бит всегда единица, и 6-ой бит всегда также единица. При этом в первом байте, если смотреть на биты слева направо (7-ой, 6-ой и так до нулевого), идет столько единиц, сколько байтов, включая первый, идет на кодирование одного Unicode символа. Заканчивается последовательность единиц нулем. А после этого идут биты самого Unicode символа. Остальные биты Unicode символа попадают во второй, или даже в третий байты (максимум три, почему — смотрите чуть ниже). Остальные байты, кроме первого, всегда идут с началом ’10’ и потом 6 битов следующей части Unicode символа.
Пример
Например: есть байты 110 10000 и второй 10 011110 . Первый — начинается с ‘110’ — это значит, что раз две единицы — будет два байта UTF-8 потока, и второй байт, как и все остальные, начинается с ’10’. А кодируют эти два байта символ Unicode, который состоит из 10100 битов от первого куска + 101101 от второго, получается -> 10000011110 -> 41E в 16-ричной системе, или U+041E в написании Unicode обозначений. Это символ большая русская О .
Сколько максимум байт на символ?
Также, давайте посмотрим, сколько максимум байт уходит в UTF-8, чтобы закодировать 16 бит кодировки Unicode. Вторые и далее байты всегда максимум могут вместить 6 бит. Значит, если начать с конечных байтов, то два байта уйдут точно (2-ой и третий), а первый должен начинаться с ‘1110’, чтобы закодировать три. Значит первый байт максимум в таком варианте может закодировать первые 4 бита символа Unicode. Получается 4 + 6 + 6 = 16 байт. Выходит, что UTF-8 может иметь либо 2, либо 3 байта на символ Unicode (один не может, так как нет надобности кодировать 6 бит (8 — 2 бита ’10’) — они будут ASCII символом. Именно поэтому первый байт UTF-8 никогда не может начинаться с ’10’).
Заключение
Кстати, благодаря такой кодировке, можно взять любой байт в потоке, и определить: является ли байт Unicode символом (если 7-ой бит — значит не ASCII), если да, то первый ли он в потоке UTF-8 или не первый (если ’10’, значит не первый), если не первый, то мы можем переместиться назад побайтово, чтобы найти первый код UTF-8 (у которого 6-ой бит будет 1), либо переместится вправо и пропустить все ’10’ байты, чтобы найти следующий символ. Благодаря такой кодировке, программы также могут, не зная Unicode, считать, сколько символов в строке (на основании первого байта UTF-8 вычислить длину символа в байтах). Вообщем, если подумать, кодировка UTF-8 придумана очень грамотно, и в то же время очень эффективно.
Кодирование информации
Любые числа (в определенных пределах) в памяти компьютера кодируются числами двоичной системы счисления. Для этого существуют простые и понятные правила перевода. Однако на сегодняшний день компьютер используется куда шире, чем в роли исполнителя трудоемких вычислений. Например, в памяти ЭВМ хранятся текстовая и мультимедийная информация. Поэтому возникает первый вопрос:
Как в памяти компьютера хранятся символы (буквы)?
Каждая буква принадлежит определенному алфавиту, в котором символы следуют друг за другом и, следовательно, могут быть пронумерованы последовательными целыми числами. Каждой букве можно сопоставить целое положительное число и назвать его кодом символа . Именно этот код будет храниться в памяти компьютера, а при выводе на экран или бумагу «преобразовываться» в соответствующий ему символ. Чтобы отличить представление чисел от представления символов в памяти компьютера, приходится также хранить информацию о том, какие именно данные закодированы в конкретной области памяти.
Соответствие букв определенного алфавита с числами-кодами формирует так называемую таблицу кодирования . Другими словами, каждый символ конкретного алфавита имеет свой числовой код в соответствии с определенной таблицей кодирования.
Однако алфавитов в мире очень много (английский, русский, китайский и др.). Поэтому следующий вопрос:
Как закодировать все используемые на компьютере алфавиты?
Для ответа на этот вопрос пойдем историческим путем.
В 60-х годах XX века в американском национальном институте стандартизации (ANSI) была разработана таблица кодирования символов, которая впоследствии была использована во всех операционных системах. Эта таблица называется ASCII (American Standard Code for Information Interchange – американский стандартный код для обмена информацией) . Чуть позже появилась расширенная версия ASCII .
В соответствие с таблицей кодирования ASCII для представления одного символа выделяется 1 байт (8 бит). Набор из 8 ячеек может принять 2 8 = 256 различных значений. Первые 128 значений (от 0 до 127) постоянны и формируют так называемую основную часть таблицы, куда входят десятичные цифры, буквы латинского алфавита (заглавные и строчные), знаки препинания (точка, запятая, скобки и др.), а также пробел и различные служебные символы (табуляция, перевод строки и др.). Значения от 128 до 255 формируют дополнительную часть таблицы, где принято кодировать символы национальных алфавитов.
Поскольку национальных алфавитов огромное множество, то расширенные ASCII-таблицы существуют во множестве вариантов. Даже для русского языка существуют несколько таблиц кодирования (распространены Windows-1251 и Koi8-r). Все это создает дополнительные трудности. Например, мы отправляем письмо, написанное в одной кодировке, а получатель пытается прочитать ее в другой. В результате видит кракозябры. Поэтому читающему требуется применить для текста другую таблицу кодирования.
Есть и другая проблема. В алфавитах некоторых языков слишком много символов и они не помещаются в отведенные им позиции с 128 до 255 однобайтовой кодировки.
Третья проблема - что делать, если в тексте используется несколько языков (например, русский, английский и французский)? Нельзя же использовать две таблицы сразу …
Чтобы решить эти проблемы одним разом была разработана кодировка Unicode.
Стандарт кодирования символов Unicode
Для решения вышеизложенных проблем в начале 90-х был разработан стандарт кодирования символов, получивший название Unicode . Данный стандарт позволяет использовать в тексте почти любые языки и символы.
В Unicode для кодирования символов предоставляется 31 бит (4 байта за вычетом одного бита). Количество возможных комбинаций дает запредельное число: 2 31 = 2 147 483 684 (т.е. более двух миллиардов). Поэтому Unicode описывает алфавиты всех известных языков, даже «мертвых» и выдуманных, включает многие математические и иные специальные символы. Однако информационная емкость 31-битового Unicode все равно остается слишком большой. Поэтому чаще используется сокращенная 16-битовая версия (2 16 = 65 536 значений), где кодируются все современные алфавиты.
В Unicode первые 128 кодов совпадают с таблицей ASCII.
Unicode (Юникод) - это стандарт кодирования символов, где каждому символу присваивается свой уникальный код, независимо от программной и аппаратной платформы.
Изначально для кодирования символов использовали 8 бит, которые дают 256 комбинаций нулей и единиц. Этого вполне достаточно чтобы закодировать весь латинский алфавит, цифры, знаки препинания, арифметические знаки, специальные управляющие символы. Стандартом стал ASCII. К тому же удобно и компактно, когда один символ равен одному байту.
Но 256 значений не достаточно, для того чтобы поместить туда еще символы других языков. Таких как греческий алфавит, кириллица, китайские иероглифы, математические символы и т.д. Что неудивительно, ведь ASCII - американский стандартный код, разрабатывался американцами, для американцев.
Уже вначале 70-х компьютеры распространились от университетов, вычислительных центров, закрытых государственных учреждений до небольших частных предприятий и домашних пользователей. США, Канада. Великобритания перестают быть монополистами в мире информационных технологий. В каждой стране есть свои вычислительные центры, IT-университеты и патенты в этой отрасли.
Как следствие, появляется огромное количество альтернативных кодировок. Ведь каждой письменности, нужны свои места в кодовой таблицы. Но вместе с тем появляется масса проблем.
Первая из них - это неправильное отображение документов одной кодировки в другой. Для того чтобы документ приобрел читабельный вид, необходимо иметь специальную таблицу, по которой машина сопоставит символы одной кодировки с другой. Для каждой пары кодировок нужна такая таблица. Альтернатива этому, использовать третью кодировку которая содержит все символы первых двух. Неудобно человеку и лишнее использование ресурсов машины.
Вторая проблема - шрифт, создается под определенную кодовую таблицу символов. Некоторые таблицы символов могут совпадать, более чем на 90%. Становиться не выгодно хранить разные шрифты, для (почти) одинаковых кодировок. Можно создавать универсальные шрифты. Но тогда потребуется хранить дополнительные данные, которые помогут разобраться, какие символы шрифта, каким символам кодировки соответствуют.
В начале 80-х кризис «крокозябры», в текстах, достиг своего пика. Необходимость в универсальной кодовой таблице стала очевидной. Нужен единый стандарт. Где поместились бы все символы. И в 1991 году такой стандарт был принят, консорциумомЮникод. Под названием Unicode. В консорциум вошли ведущие IT-предприятия, которые и определили, какой должна быть единая кодировка.
Если использовать кодировку с переменной шириной, то изначально потребуется дополнительные алгоритмы. Которые определят сколько нужно байт для хранения того или иного символа. Необходимо иметь алгоритмы которые, в цепочке бит, будут вычислять где конец, текущего символа, и где начало следующего. Решили, что все это будет сложно, и ввели кодировку с фиксированной шириной. Кодировка с переменной шириной, позволяет использует столько бит, сколько необходимо для хранении символ. Она намного компактнее. Этим фактором, изначально пренебрегли.
Сколько нужно бит, для нового стандарта Unicode? 8 бит даст 256 значений для символов (2 8 = 256). Практика доказала, это мало. 32 бита даст (2 32 = 2 294 967 296) позиций для символов. Это много. Слишком, не эффективное использование машинной памяти. Оптимальным вариантом, это взять 16 бит (2 16 = 65536). Таким образом первая версия Unicode была фиксированной шириной 16 бит. В нее вошли не все символы, а только самые употребляемые, содержавшиеся ране в известных кодировках. Например, в Unicode не попали, редко используемые китайские иероглифы. И не которые символы из высшей математики.
Каждый символ Unicode имеет свой порядковый номер. Который по стандарту записывается шестднадцатеричным числом.
Последние версии Unicode были сильно изменены. И первое, что решили, это хранить все существующие символы в данной кодировке. Символы в Unicode стали переменной длинны. Кодовую таблицу разбили на два пространства. В первом, хранят все наиболее употребляемые символы. Это в диапазоне от 0 до 65535. Остальное пространство используется для редко употребляемых символов. Любой символ можно представить несколькими кодами. Поэтому существующую таблицу Unicode постоянно нормализируют и выпускают новые версии. Современный Unicode поддерживает письменность слева направо так и на оборот справа налево, арабские символы. Он даже позволяет создавать двунаправленные тексты. Т.е в тексте, относящейся к одной кодовой таблице, могут содержаться символы, пишущиеся как справа налево так и наоборот. Но эту возможность должны поддерживать и аппаратные устройства.
В Unicode включает в себя не только символы различных языков. Но и узкоспециализируемые математические символы, ноты. В нем содержится все современные письменности. И даже редко используемые такие как коптское письмо, чероки, эфиопское. Для академических, кругов в кодовую таблицу, добавили даже вымершие письменности. Например: клинопись, руны, египетские иероглифы, этрусский алфавит.
Начиная с конца 60-х годов, компьютеры все больше стали использоваться для обработки текстовой информации и в настоящее время большая часть персональных компьютеров в мире (и наибольшее время) занято обработкой именно текстовой информации.
ASCII - базовая кодировка текста для латиницы
Традиционно для кодирования одного символа используется количество информации, равное 1 байту , то есть I = 1 байт = 8 битов.
Для кодирования одного символа требуется 1 байт информации. Если рассматривать символы как возможные события, то можно вычислить, какое количество различных символов можно закодировать: N = 2I = 28 = 256.
Такое количество символов вполне достаточно для представления текстовой информации, включая прописные и строчные буквы русского и латинского алфавита, цифры, знаки, графические символы и пр. Кодирование заключается в том, что каждому символу ставится в соответствие уникальный десятичный код от 0 до 255 или соответствующий ему двоичный код от 00000000 до 11111111.
Таким образом, человек различает символы по их начертаниям, а компьютер - по их кодам. При вводе в компьютер текстовой информации происходит ее двоичное кодирование, изображение символа преобразуется в его двоичный код.
Пользователь нажимает на клавиатуре клавишу с символом, и в компьютер поступает определенная последовательность из восьми электрических импульсов (двоичный код символа). Код символа хранится в оперативной памяти компьютера, где занимает один байт. В процессе вывода символа на экран компьютера производится обратный процесс - декодирование, то есть преобразование кода символа в его изображение. В качестве международного стандарта принята кодовая таблица ASCII (American Standart Code for Information Interchange) Таблица стандартной части ASCII Важно, что присвоение символу конкретного кода - это вопрос соглашения, которое фиксируется в кодовой таблице. Первые 33 кода (с 0 по 32) соответствуют не символам, а операциям (перевод строки, ввод пробела и так далее). Коды с 33 по 127 являются интернациональными и соответствуют символам латинского алфавита, цифрам, знакам арифметических операций и знакам препинания. Коды с 128 по 255 являются национальными, то есть в национальных кодировках одному и тому же коду соответствуют различные символы.
К сожалению, в настоящее время существуют пять различных кодовых таблиц для русских букв (КОИ8, СР1251, СР866, Mac, ISO), поэтому тексты, созданные в одной кодировке, не будут правильно отображаться в другой.
В настоящее время широкое распространение получил новый международный стандарт Unicode, который отводит на каждый символ не один байт, а два, поэтому с его помощью можно закодировать не 256 символов, а N = 216 = 65536 различных
Юникод - появление универсальной кодировки текста (UTF 32, UTF 16 и UTF 8)
Эти тысячи символов языковой группы юго-восточной Азии никак невозможно было описать в одном байте информации, который выделялся для кодирования символов в расширенных кодировках ASCII. В результате был создан консорциум под названием Юникод (Unicode - Unicode Consortium) при сотрудничестве многих лидеров IT индустрии (те, кто производит софт, кто кодирует железо, кто создает шрифты), которые были заинтересованы в появлении универсальной кодировки текста.
Первой кодировкой текста, вышедшей под эгидой консорциума Юникод, была кодировка UTF 32 . Цифра в названии кодировки UTF 32 означает количество бит, которое используется для кодирования одного символа. 32 бита составляют 4 байта информации, которые понадобятся для кодирования одного единственного символа в новой универсальной кодировке UTF 32.
В результате чего один и то же файл с текстом, закодированный в расширенной кодировке ASCII и в кодировке UTF 32, в последнем случае будет иметь размер (весить) в четыре раза больше. Это плохо, но зато теперь у нас появилась возможность закодировать с помощью UTF 32 число символов равное двум в тридцать второй степени (миллиарды символов, которые покроют любое реально необходимое значение с колоссальным запасом).
Но многим странам с языками европейской группы такое огромное количество символов использовать в кодировке вовсе и не было необходимости, однако при использовании UTF 32 они ни за что ни про что получали четырехкратное увеличение веса текстовых документов, а в результате и увеличение объема интернет трафика и объема хранимых данных. Это много и такое расточительство себе никто не мог позволить.
В результате развития универсальной кодировки Юникод появилась UTF 16 , которая получилась настолько удачной, что была принята по умолчанию как базовое пространство для всех символов, которые у нас используются. UTF 16 использует два байта для кодирования одного символа. Например, в операционной системе Windows вы можете пройти по пути Пуск - Программы - Стандартные - Служебные - Таблица символов.
В результате откроется таблица с векторными формами всех установленных у вас в системе шрифтов. Если вы выберите в Дополнительных параметрах набор символов Юникод, то сможете увидеть для каждого шрифта в отдельности весь ассортимент входящих в него символов. Кстати, щелкнув по любому из этих символов вы сможете увидеть его двухбайтовый код в кодировке UTF 16, состоящий из четырех шестнадцатеричных цифр:

Сколько символов можно закодировать в UTF 16 с помощью 16 бит? 65 536 символов (два в степени шестнадцать) было принято за базовое пространство в Юникод. Помимо этого существуют способы закодировать с помощью UTF 16 около двух миллионов символов, но ограничились расширенным пространством в миллион символов текста.
Но даже удачная версия кодировки Юникод под названием UTF 16 не принесла особого удовлетворения тем, кто писал, допустим, программы только на английском языке, ибо у них после перехода от расширенной версии кодировки ASCII к UTF 16 вес документов увеличивался в два раза (один байт на один символ в ASCII и два байта на тот же самый символ в кодировке UTF 16). Вот именно для удовлетворения всех и вся в консорциуме Юникод было решено придумать кодировку текста переменной длины .
Такую кодировку в Юникод назвали UTF 8 . Несмотря на восьмерку в названии UTF 8 является полноценной кодировкой переменной длины, т.е. каждый символ текста может быть закодирован в последовательность длинной от одного до шести байт. На практике же в UTF 8 используется только диапазон от одного до четырех байт, потому что за четырьмя байтами кода ничего уже даже теоретически не возможно представить.
В UTF 8 все латинские символы кодируются в один байт, так же как и в старой кодировке ASCII. Что примечательно, в случае кодирования только латиницы, даже те программы, которые не понимают Юникод, все равно прочитают то, что закодировано в UTF 8. Т.е. базовая часть кодировки ASCII перешла в UTF 8.
Кириллические же символы в UTF 8 кодируются в два байта, а, например, грузинские - в три байта. Консорциум Юникод после создания кодировок UTF 16 и UTF 8 решил основную проблему - теперь у нас в шрифтах существует единое кодовое пространство. Производителям шрифтов остается только исходя из своих сил и возможностей заполнять это кодовое пространство векторными формами символов текста.
