Примитивные символьные данные
6.1 Кодировки
6.1.1. Представление символов целыми числами
Как уже указывалось в "лекции 5" , все символы в ЭВМ представлены в виде целых чисел. Порядок соответствия литер символов целым числам называется кодировкой (или кодовой таблицей, таблицей символов). В зависимости от размера таблицы символов разрядность этих целых чисел равна 1 байт (для кодировок EBCDIC и всех вариантов ASCII) , 2 байта (для кодировок Unicode) или переменной длины (UTF-8, UTF-7 и др.). Более подробно о кодировках будет сказано ниже.
6.1.2. Кодировки
Вначале возникли кодировки, включающие в себя десятичные цифры, буквы (прописные и строчные) латинского алфавита, сокращения английского языка (такие как "&" и "@"), знаки препинания и специальные символы. На основе этих символов возникли кодировки EBCDIC (более старая) и ASCII (более новая, используемая до сих пор). Преимущества кодировки ASCII в том, что расположение латинских букв в ней не только упорядочено по алфавиту (как это сделано во всех кодировках), но и расположены эти буквы "без разрывов", то есть присутствует сплошная нумерация букв от A до Z и от a до z. Это позволяет лучше реализовать алгоритмы сравнения символов и сортировку строк символов.
Кодировки EBCDIC и ASCII являются 7-ми битными кодировками, хотя они и занимают 1 байт. Самый старший (нулевой) бит является либо резервным, либо используется для контроля четности при передаче данных.
6.1.3. Восьмибитные кодировки
Однако в кодировке ASCII отсутствуют даже некоторые символы (умляуты) западноевропейских алфавитов. Что же говорить о кириллических символах... Для решения этой проблемы были созданы расширенные кодировки ASCII, в которых для представления символов используются все восемь бит. Это доводит количество закодированных символов до 256. Среди реализаций кириллических символов в расширенных ASCII кодировках можно отметить кодировки OEM 866 (MS-DOS), ANSI cp 1251 (Windows) и KOI8-R (все клоны UNIX и VAX).
Относительно полно концепция "сплошной нумерации" применяется в кодировке USSR GOST и ANSI cp 1251. Кодировка USSR GOST должна была заменить кодировку OEM 866 на компьютерах, производимых в СССР. Однако, по ряду причин, этого не произошло, и в настоящее время эта кодировка является "мертвой".
Исторически первой кодировкой с кириллическими символами была кодировка KOI8. В ней латинские буквы с кодами 128-255 были заменены кириллическими символами в той же транскрипции. К сожалению, эта кодировка сейчас почти сошла на "нет" по следующим причинам:
- Этот стандарт не поддержали производители компьютерного оборудования за рубежом;
- Отсутствие "сплошной" нумерации кириллического алфавита;
- Невозможность совместного использования кириллицы и умляутов;
- "Неправильная" реализация символов псевдографики.
Следом за этой кодировкой на российские компьютеры вначале пришла болгарская, а затем - и альтернативная русская кодировка ДОС, впоследствии переименованная в OEM 866. Преимущества этой кодировки:
- Компактность;
- Частично сплошная нумерация символов;
- Полная совместимость с MS-DOS;
- Корректная реализация символов псевдографики;
- "Нормальная" обработка символов на всех платформах;
- Наличие коммерческих и некоммерческих знакогенераторов для этой платформы.
Недостатками этой кодировки являются:
- Привязанность к MS-DOS, вместе с которой эта кодировка "умирает";
- Невозможность совместного использования кириллицы и умляутов;
- Общий недостаток всех восьмибитных кодировок - ограничение на кодирование только 256-ю символами.
Кодировка ANSI cp 1251 получила широкое распространение из-за продвижения продукции корпорации Microsoft, прежде всего Microsoft Windows и Microsoft Word, на Российский рынок. Эта кодировка имеет большие преимущества перед другими кириллическими кодировками:
- Компактность;
- Сплошная нумерация символов (кроме "е" и "Ё");
- Стандартность.
Однако у этой кодировки есть недостатки:
- Привязанность в основном к платформе Microsoft Windows;
- Некорректная обработка многими программами символов с номерами 254 и 255 ("ю" и "я");
- Невозможность совместного использования кириллицы и умляутов.
6.1.4.Многоразрядные кодировки
К "многоразрядным" кодировкам относятся кодировки Unicode и UTF. Рассмотрим историю их возникновения.
Уже в начале 90-х годов XX века стало ясно, что 256 чисел для кодирования символов очень мало. Например, с помощью этих чисел нельзя кодировать все символы европейских алфавитов. Поэтому появились русские, западноевропейские, восточноевропейские, балтийские, не говоря уже об арабских, турецких кодировках и кодировках на иврите, каждая из которых содержала 256 символов. Что же говорить о вьетнамском алфавите, корейских, японских и китайских иероглифах, каждые из которых содержат более 256 символов? Для этой цели был разработан специальный стандарт на кодировку символов 16-ти разрядными целыми числами (2 байта) - Unicode. Полностью с помощью 16-ти разрядных чисел можно представить более 65 тысяч символов, что с избытком хватит для кодирования всех литер основных языков земли.
При всей полезности кодировки символов Unicode следует отметить ее недостаток - вдвое больший размер кода, чем при использовании 8-ми разрядных кодировок. Этот недостаток особенно заметен при использовании только латинских букв. Поэтому вскоре был принят стандарт на кодировку UTF. В ней символы кодируются числом переменной разрядности: 7-ми разрядными кодами ASCII для латинских букв и 16-ти разрядными символами Unicode для всех остальных литер алфавита. За эту "экономию" приходится расплачиваться людям, пишущих на отличном от английского языка - работа с символами в кодировке UTF более сложная, чем в других кодировках. Это надо помнить!
Примечание. Кодировка UTF-8 стала стандартом в операционной системе Linux и в Интернете. При разработке приложений в Microsoft Window, начиная с версии 98, также широко используются кодировки UTF-8 и UTF-7.
6.2. Примитивные символьные данные
В качестве "примитивных" символьных данных чаще всего используются массивы "символьных" типов данных. При этом максимальная длина этого массива (то есть максимальный индекс, который может принимать этот массив) фиксируется либо программистом, либо системой программирования (например, конкретной реализацией компилятора).
В таблице 6.1 даются описания символьных данных, их размерности и обозначение.
Обычно, в начале работы программы, весь массив символьных данных заполняют нулевой константой (она обозначается на языке Си как " \0 "). Это делается в расчете на то, что признаком конца символьных данных является не признак конца строки, не пробельный символ, а именно этот нулевой символ. Поэтому заполнение массива нулевыми символами заведомо гарантирует, что не случится ошибка типа размещения символьных данных за границами массива, после одиночного нулевого символа.
Что касается максимальной длины символьной строки, то она может принимать значения от 255 (языки "Паскаль", "Quick Basic", СУБД dBase III+), 2047 (Multi Edit и часть редакторов) и даже ~ 64 Кбайт (величина сегмента памяти процессора x86). Но, следующий нехитрый расчет показывает, что использовать длину строки более 2000 (80*25) символов нерационально (она все равно не уместится на экране).
Также автор советует не доверять своей памяти в использовании максимальной длины строки, а определить ее в виде константы целого значения (например, константного типа const unsigned short на языке Си).
Следующим "джентльменским соглашением" при работе с символьными данными является неизменность значений массивов символьных данных (кроме, возможно, первого массива в выражении) при символьных вычислениях. Вот почему большинство символьных функций принимает тип аргументов как const char * (указатель на начало массива символьных констант). Именно к этому типу необходимо принудительно приводить Ваши переменные в программе!
Итак, организация работы с "примитивными" символьными массивами данных следующая:
- Создается (функцией malloc ) или объявляется (в начале блока программ) необходимое количество символьных массивов-переменных;
- Они инициализируются нулевыми символами (функцией memset );
- Производятся операции с символьными данными:
- ввод;
- присвоение им константных значений;
- замена, добавление или "обрезание" значений этих переменных;
- вывод символьных значений;
- Для созданных переменных при завершении программы необходимо освободить память, размещенную при их создании (функцией free ).
Собственно стандартные функции для работы с символьными типами данных (в том числе и с примитивными данными) на языках Си и C++, вместе с комментариями, приведены в таблице 6.2.