Языки, символы и кодировки.

Here is the whole set! a character dead at every word.
- Richard Brinsley Sheridan, The School for Scandal,    
Act 2, scene 2    

  1. Атрибут Language
  2. Что такое Character ?
  3. Encoding
  4. CharSet
  5. Поток байтов
  6. Стандарты
  7. POSIX

Атрибут Language

    Информация, полученная из "человеческого" мира и предназначенная для машинной обработки, как правило имеет специальный атрибут  : язык или language (lang). Атрибут language чрезвычайно важен при выполнении задач хранения, поиска и классификации информации. Данный атрибут может иметь не только текстовая, но например audio информация:

Content-Description: Russian Argo audio sample :-)
Content-Type: audio/basic
Content-Language: ru

    Однако нас интересует именно текстовая информация.

    Большинство языков мира имеют письменнось, а некоторые языки даже несколько (например кириллица и глаголица для славянских языков или kana и kanji в японском и т.д.). Определенная система письменности называется script. Системы письменности существуют самые разнообразные (например узелковое :-), но большинство письменностей - это изображение последовательности специальных символов (character).

* ПРИМЕЧАНИЕ : Объяснение концепции символа выходит далеко за рамки данного документа. Мы не будем вдаваться в филологические и философские подробности, а рассмотрим лишь узкий аспект -- способы представления символов (национального) языка для автоматической (машинной) обработки. Тогда термин "символ" (character) можно определить как "единицу текстовой информации" (unit of textual information), которая передается письменно и участвует в машинной обработке. Очень важно четко представлять себе, что речь идет об "абстрактном" символе.

    Простановка атрибута lang в текстах осуществляется средствами MIME (RFC-1766). Все современные языки разметки (HTML 4.0, XHTML, XML) имеют специальный атрибут LANG="" практически у любого тэга (например <P LANG="jp">). Сами коды языков описаны в стандарте ISO-639.


Character

    Каждый "абстрактный" символ имеет изображение -- glyph. Считается, что каждый символ имеет "каноническое" изображение , то есть такое, которое позволяет однозначно идентифицировать данный символ, то есть распознать и отличить его от других. Таким образом, в модели POSIX и UNICODE не уделяется никакого внимания вариантам начертания символа, то есть шрифтам (fonts) во всем их многообразии, . Поэтому все, что изображено на примере ниже, будет одним и тем же "абстрактным" символом  :
ЛАТИНСКАЯ ЗАГЛАВНАЯ БУКВА А    (LATIN CAPITAL LETTER A):

A a A a A a

    В стандарте UNICODE каждому определенному символу присвоено определенное имя и номер :

  UNICODE Character Name
A U+0041 LATIN CAPITAL LETTER A
a

U+0061

LATIN SMALL LETTER A
Ю

U+042E

CYRILLIC CAPITAL LETTER YU
1

U+0031

DIGIT ONE
+

U+002B

PLUS SIGN
O

U+03A9

GREEK CAPITAL LETTER OMEGA

boxd.gif (862 bytes)

U+2569

BOX DRAWINGS DOUBLE UP AND HORIZONTAL
    и так далее.

    В настоящее время в стандарт UNICODE, входят практически все употребляемые символы (~40.000), и им соответственно присвоены стандартные имена. Последнее значительное изменение -- введение символа валюты EURO в сентябре 1998 г.

    Посмотреть набор символов UNICODE можно здесь : http://charts.unicode.org/ .

    Таким образом, для нас символ (character) - это единица текстовой информации, имеющая определенное изображение и определенное имя.


Encoding.

    Наиболее важными понятиями при обработке символов являются понятии Coded Character Set (CCS) и Character Encoding Scheme (CES).

    Давайте попробуем разобраться, что это такое.

    Чаще всего набор символов определяется из языка (lang) и соответствующей ему системы письменности (script) и алфавита.

    Для автоматической обработки, хранения и передачи символов необходимо каждый "абстрактный" символ перевести в числовую форму для размещения в ячейках ЭВМ.

  С "программистской" точки зрения это задача совершенно тривиальна. Нужно просто присвоить каждому символу (абстрактному !) определенное число, которое и хранить в памяти ЭВМ, то есть закодировать символ. Например, условимся, что символу 'A' - LATIN CAPITAL LETTER A соответствует число (код) 61. И наоборот, условимся , что число (integer) 61 будет означать ни что иное, как символ 'A'. Таким образом образуется пара (61, 'A') число - символ. И наоборот, символ - число. Соответствие однозначное.

    Пусть у нас теперь есть набор символов : character repertoire {'A','B','C'} (это маленькое подмножество символов латинского алфавита). Продолжим кодирование. Тогда из этого множества {'A','B','C'} у нас образуется множество пар : {(61,'A'),(62,'B'),(63,'C')}.

    Давайте рассмотрим подробнее. Итак :

'A' - это абстрактный символ, "character"
набор символов {'A','B','C'} - "character repertoire"
число 61 - это "code point".
набор чисел {61,62,63} - это "codeset" или "code space".
набор пар число-символ {(61,'A'),(62,'B'),(63,'C')} - это CCS "coded character set"

    Фактически, CCS можно рассматривать как базу данных, в которой хранятся a)символы, b)коды; в виде обычной таблицы - map (charmap). Тогда допустимы операции :

CСS('A')=61
CСS(61)='A'

    Фактически это будет обозначать операции выборки из некоей "базы" :

SELECT CODE FROM CCS WHERE CHAR='A'
SELECT CHAR FROM CCS WHERE CODE=61

    Естественно, CCS-ов (наборов символов) существует огромное множество : ASCII, KOI8-R, ISO_8859-1 или даже UNICODE (UCS).


СharSets

    Давайте теперь рассмотрим собственно наборы символов. Первое что мы должны сделать - это обратить внимание на терминологию. Если мы говорим о наборе "абстрактных" символов, то употребляется термин character repertoire.

    Каким образом задается character repertoire ?

    Как мы уже ранее выяснили, существуют определенные "наборы символов" для каждого конкретного языка (алфавит). Иногда рассматривается набор символов, воспроизводимый конкретной аппаратурой (например DEC VT-100 Character Set или HP Latin1).

      Часто конкретный набор символов является подмножеством другого, более обширного набора символов или же комбинацией нескольких наборов (или их частей). Например, широко распространенный набор символов для представления русского языка : KOI8-R, содержит в себе символы из наборов LATIN, CYRILLIC, BOX DRAWING, BLOCK ELEMENT и т.д.. А конкурирующий с ним charset - Windows-1251 содержит больше символов из набора CYRILLIC (русские, украинские, белорусские), но меньше - из BOX и BLOCK. Кроме того в KOI8-R нет например символа EURO.

koi8-r.gif (8338 bytes)
Старшая половина (CCS) KOI8-R и code points символов.
Младшая половина совпадает с US-ASCII.
(см. также описние KOI8-R в формате POSIX.)

    Существует минимальный (переносимый) (POSIX) Portable Charset - набор символов, который должны поддерживать любые информационные системы (определен в стандарте ISO 646) (он же ASCII). С другой стороны, существует также "универсальный" Universal Character Set (UCS, UNICODE) включающий в себя все возможные символы человеческих языков, технические, картографические и т.д. символы (~40.000 символов) (ISO 10646). Также, для примера, можно упомянуть один из довольно широко распространенных наборов символов : LATIN-1 (Czyborra ISO 8859-1).


Поток байтов.

    Для того, чтобы вывести поток чисел (code point) для хранения на внешнее устройство ЭВМ, его нужно преобразовать в поток байтов.

* ВАЖНОЕ ЗАМЕЧАНИЕ : Современная вычислительная техника устроена таким образом, что минимальной единицей хранения и ввода/вывода из программы на внешние устройства (read/write) является 8-ми битный БАЙТ.

    Чтобы выполнить такое преобразование применяется схема кодирования : CES "character encoding scheme". Продолжим :

соответствие : символ 'A' <--> байт со значением 61 - это CES "character encoding scheme", или просто encoding

    Теперь насчет кодирования. Каким образом выбирается CES и осуществляется кодирование ?

  Как работает CES ?

    Очень просто. Пусть у нас есть поток символов ("абстрактных") : "ABBACABCC". Поскольку у нас задан CCS, то применение схемы кодирования CES к этому потоку (тексту) "ABBACABCC" будет равно применению его CES к каждому символу в потоке :
CES('A') CES('B') CES('B') CES('A')...
и мы получим поток чисел (кодов), а далее поток байт : 61 62 62 61 63 61 62 63 63.
Аналогично, применение CES к потоку байт даст поток чисел (кодов) и далее, поток "абстрактных символов".

   Схемы кодирования (CES) бывают самые разнообразные. Для маленьких наборов символов (типа US-ASCII) применяют очень простые схемы, как правило прямое соответствие : код(code point) = байт. Для больших наборов довольно сложные и нетривиальные, типа ISO-2022.

    А теперь внимание ! Вот это самое пресловутое "множество пар" имеет колоссальное значение ! Формально оно называется CCS : coded character set. Именно ему присваивается имя : ASCII, ISO_8859-5 или KOI8-R ! Перечитать еще раз !

 

    Выполнить "кодирование" (encoding) довольно легко как для латинских так и для всех (?) индоевропейских языков (фонетическое письмо). Действительно, закодировать 26 латинских (ASCII) или 33 русских буквы не составляет труда (даже в варианте заглавных и прописных, плюс цифры, плюс знаки препинания). Количество символов (character repertoire) мало, соответственно не велико и code space и получается меньше 256 сode points, что позволяет уместить их в один байт (2^8=256 различных кодовых позиций).

   Для языков, чья письменность построена по идеографическому (иероглифическому) принципу, ситуация несколько сложнее. Например в современном японском языке целых 1850 "официальных" иероглифов, тогда как в китайском их число доходит до 5000. Это вызывает определенные трудности при работе вычислительных систем, поскольку одного байта, то есть 265 кодовых позиций мало.

    В настоящее время в подавляющем большинстве charset-ов (наборов символов) применяется очень простое 8-ми битное (байтовое) кодирование. При этом в поток символов выводится просто число : номер кодовой позиции (code point). Точно такие же цепочки чисел (1..255) хранятся в памяти ЭВМ. Все настолько к этому привыкли, что charset-ы с большим количеством символов (>256) иногда называют large charset.

    Когда мы работаем с большими наборами символов, то нам в первую очередь необходимы обширные CCS, например EUC-JP или UNICODE. В этом случае мы сталкиваемся с трудностями хранения, передачи и обработки чисел с кодовыми позициями >256. Для поддержки таких наборов символов в стандарте POSIX введены механизмы Multibyte (как правило для ввода-вывода) и Wide Class chars (для хранения в памяти ЭВМ и обработки).

    Механизмы Multibyte предназначены для преобразования больших чисел (кодовых позиций) в поток байтов. Например, для передачи набора символов UNICODE (UCS, ISO/IEC 10646-1) наиболее часто применяется формат UTF-8 (UNICODE Transformation Format RFC-2279, www.utf-8.com ), а для азиатских наборов символов : ISO-2022.

    Проблемы могут возникнуть также при необходимости создания многоязычных текстов, например русско-японско-английских, или содержащих русский и иврит, e.t.c. В этом случае - практически единственное решение : UNICODE и HTML/XML атрибутами LANG="".

    Особый класс проблем возникает и в том случае, если кодировка текста (последовательности байт) заранее неизвестна  (потеряна CES), так как в UNIX, да и в других OS файл не имеет никаких дополнительных атрибутов. Это также актуально для HTTP, в том случае, если кодировка (CES) файла .HTML неизвестна. Это довольно неудобно, поскольку часто единственный способ - это подбор (или угадывание) подходящей кодировки... Вывод : указывать кодировку необходимо.

 

    Отсюда следует один очень простой вывод : при хранении текста (потока байтов) мы должны также хранить CCS+CES ! А вот где его хранить - это вопрос. Можно в том же потоке ( "In-band" или "MARK-UP" способ). Можно где-то снаружи потока, или параллельным "информационным" потоком  ("Out-band" способ). Можно хранить лишь имя (ссылку на) CCS.

    К сожалению, в стандартном POSIX (например на stdin/stdout) мы имеем только потоки кодов. А вся информация о CCS потеряна. Подробнее про это можно прочитать в  статье о локализации POSIX.

 

    В контексте стандарта MIME  мы говорим наборе символов вместе с их кодами и схемой преобразования символы<-->байты. то речь идет о  coded character set. Именно этому набору : coded character set и присваивается имя : KOI8-R, ISO_8859-1, ASCII. Иногда термин CCS сокращают до сharset.

    Так например в стандарте MIME употребляется термин сharset, хотя подразумевается конечно же CCS:
Content-Type: text/plain; charset=koi8-r
Content-Type: text/plain; charset=Windows-1251

Content-Type: text/plain; charset=ibm-866

   Каждый charset имеет определенное имя.


Standarts

    Существует несколько стандартов описания сharset и способов их кодирования :

    Описание наборов символов KOI8-R, CP1251, IBM866 в формате POSIX.

    Что касается имен зарегистрированных Charset-ов, то смотри здесь.


POSIX

    Сначала - немного критики... 

    Теперь по делу.

    Кроме установки разных значений локализации en_US или ru_RU в системе POSIX могут употребляться и различные значения Charset-а, например ru_RU.KOI8-R и ru_RU.ISO8859-5.

    Для установки в POSIX-системе новой locale c другим набором символов (charset) применяется утилита localedef для  компиляции Файла Описания Локализации и Файла Описания Набора Символов (Character Set Description File).

$ localedef -c -i ru_RU -f KOI8-R ru_RU.KOI8-R

    Для проверки установленных в POSIX-системе charset-ов применяется утилита locale :

$ locale -m
$ locale charmap

    Для преобразования потока символов из одного charset в другой, в стандарте POSIX существуют утилита iconv и функция iconv().

    В системе Plan 9 существует утилита tcs.


    Дополнительная информация.


Содержание "Locale AS IT IS"


Last change : 22-04-2003