|
Матрёшка: обмен текстовыми даннымиСтроковые текстовые данные в Матрёшке всегда хранятся в виде объектов Universal_String с использованием набора символов Unicode. В то же время для хранения и обмена текстовыми данными активно используются самые разнообразные кодировки текстовых данных. Матрёшка включает специальный компонент — текстовый кодек — для выполнения преобразования текстовых данных из/во внутреннее представление. Для представления внешних данных используется стандартный тип для представления элемента потока ввода‐вывода — Ada.Streams.Stream_Element — и массив элементов этого типа Ada.Streams.Stream_Element_Array. Матрёшка дополнительно предоставляет неограниченный вектор — League.Stream_Element_Vectors.Stream_Element_Vector. Преимущество использования последнего очевидно в случаях когда заранее неизвестно и сложно в процессе выполнения определить размер данных. Менее известно другое (пожалуй более важное) преимущество: объект Stream_Element_Vector имеет фиксированный размер и занимает фиксированный объём памяти стэка не зависимо от того, какого объёма данные в него помещены. Для современных многонитевых приложений это имеет важное значение — на размер стэка каждой нити накладываются подчас весьма серьёзные ограничения. Такое немного необычное представление выбрано не случайно, оно позволяет провести чёткую границу между текстом как таковым и его внешним представлением. Приложение не делает никаких предположений как входные и выходные данные преобразуются к тексту, что значительно улучшает готовность приложений к работе в средах различных естественных языков. Создание текстового кодека и выбор кодировкиДля использования текстового кодека необходимо создать и инициализировать объект типа League.Text_Codecs.Text_Codec. Делается это с помощью функции League.Text_Codecs.Create, принимающей единственный параметр — имя используемой кодировки. В качестве имён можно привести «ISO-8859-1» или «Windows-1251». Полный список поддерживаемых кодировок и их имена приведены в документации на Матрёшку. При поиске необходимого кодека имя кодировки подвергается нормализации — из него удаляются служебные символы и все буквы приводятся к одному регистру; поэтому „utf8“ и „UTF-8“ есть корректное имя одной и той же кодировки. Ниже приведён пример кода создания и инициализации текстового кодека: Codec : League.Text_Codecs.Text_Codec := League.Text_Codecs.Codec (League.Strings.To_Universal_String ("Windows-1251")); Преобразование текстовых данных во внешнее представлениеПредположим, что у нас имеется некоторая строка и необходимо преобразовать её во внешнее представление используя кодировку UTF-8. Для преобразования строки в вектор элементов потока предназначена функция Encode. Для полноты примера ещё потребуется функция преобразования вектора в массив — To_Stream_Element_Array. function To_Utf8 (Item : League.Strings.Universal_String) return Ada.Streams.Stream_Element_Array is Codec : League.Text_Codecs.Text_Codec := League.Text_Codecs.Codec (League.Strings.To_Universal_String ("UTF-8")); begin return Codec.Encode (Text).To_Stream_Element_Array; end To_Utf8; Преобразование текстовых данных из внешнего представленияОбратное преобразование выполняется столь же просто с использованием функции Decode. Эта функция перегружена и для удобства использования может принимать объекты Stream_Element_Array или Stream_Element_Vector. function From_Windows_1251 (Item : Ada.Streams.Stream_Element_Array) return League.Strings.Universal_String is Codec : League.Text_Codecs.Text_Codec := League.Text_Codecs.Codec (League.Strings.To_Universal_String ("Windows-1251")); begin return Codec.Decode (Item); end From_Windows_1251; Рекомендованный кодек для ввода‐выводаПеред любым интернационализированным приложением встаёт нелёгкий выбор: в какой кодировке поступают входящие данные и какая кодировка должна использоваться для вывода данных. Хорошо если ответ на этот вопрос может быть найден некоторым способом, например в заголовке XML файла или имени кодировке в заголовке HTTP ответа. А что же делать «простым» приложениям? Ответ на этот вопрос даёт функция League.Text_Codecs.Codec_For_Application_Locale, которая возвратит кодек предназначенный для обработки данных в соответствии с используемыми настройками локали пользователя. String, стандартная библиотека и подводные камниКак всем известно, традиционный тип для представления текстовых данных в Ada есть тип String. Что заключают в себя объекты этого типа — неведомо никому, зато он сплошь и рядом используется в стандартной библиотеке. И хотя последующие поколения языка предложили другие типы данных (Wide_String и Wide_Wide_String) для представления текстовых данных, это почему‐то никак не повлияло на многие подпрограммы стандартной библиотеки языка, которые по прежнему работают с типом String. В частности, имя файла даже в пакете Ada.Wide_Wide_Text_IO может быть задано только с помощью типа String. Сообщения для исключенийЕщё одним очень досадным местом использования String является текстовое сообщение оператора возбуждения исключения. Для удобства использования пакет League.Text_Codecs предоставляет подпрограмму To_Exception_Message, способную корректно преобразовать текстовые данные в понятный оператору возбуждения исключения формат. procedure Raise_With_Message (Message : League.Strings.Universal_String) is begin raise Program_Error with League.Text_Codecs.To_Excpetion_Message (Message); end Raise_With_Message; Грязный хакВвиду того, что без преобразования в String иногда не обойтись и в качестве заключения приводится реализация двух «полезных» функций преобразования между Universal_String и String. Постарайтесь НИКОГДА не использовать их без явной на то необходимости! function To_String (Item : League.Strings.Universal_String) return String is Aux : constant Ada.Streams.Stream_Element_Array := League.Text_Codecs.Codec_For_Application_Locale.Encode (Item); Result : String (1 .. Aux'Length); for Result'Address use Aux'Address; begin return Result; end To_String; function To_Universal_String (Item : String) return League.Strings.Universal_String is Aux : Ada.Streams.Stream_Element_Array (1 .. Item'Length); for Aux'Address use Item'Address; begin return League.Text_Codecs.Codec_For_Application_Locale.Decode (Aux); end To_Universal_String; Автор: Вадим Годунко |