Матрёшка: строки и Unicode

В предыдущей статье (см. Матрёшка: символы и строки) было рассмотрено использование предоставляемых Матрёшкой строк, однако за рамками рассмотрения остались некоторые функции, тесно связанные со спецификацией Unicode как то преобразование регистра символов, сопоставление строк, нормализация и т.д. Далее в статье будет рассмотрено использование этих подпрограмм и даны некоторые рекомендации.

Сопоставление

Сопоставление строк есть особый вариант сравнения строк, позволяющий упорядочить сткроки в порядке, ожидаемом пользователем приложения. Алгоритм сопоставления достоточно сложен и ресурсоёмок, но эти затраты полностью окупаются удобством использования конечного приложения. Важно отметить, что алгоритм сопоставление принимает во внимание настройки языка конечного пользователя.

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

Для предстваления ключа сортировки используется тип League.Strings.Sort_Key и получить ключ сортировки для строки с учётом текущих настроек языка пользователя можно с помощью подпрограммы Collation. Теперь настало время для примера: создадим два упорядоченных набора строк, для упорядочивания одного будет использоваться обычное бинарное стравнение, а для упорядочивания второго — сопоставление.


          

          
with Ada.Containers.Ordered_Sets;
with Ada.Wide_Wide_Text_IO;

with League.Strings;

procedure Collation is

   use type League.Strings.Sort_Key;
   
   package Binary_Order_Sets is
     new Ada.Containers.Ordered_Sets
          (League.Strings.Universal_String,
           League.Strings."<",
           League.Strings."=");

   function Less
    (Left  : League.Strings.Universal_String;
     Right : League.Strings.Universal_String) return Boolean;
   
   function Equal
    (Left  : League.Strings.Universal_String;
     Right : League.Strings.Universal_String) return Boolean;
   
   package Collation_Order_Sets is
     new Ada.Containers.Ordered_Sets
          (League.Strings.Universal_String, Less, Equal);
   
   -----------
   -- Equal --
   -----------
   
   function Equal
    (Left  : League.Strings.Universal_String;
     Right : League.Strings.Universal_String) return Boolean is
   begin
      return Left.Collation = Right.Collation;
   end Equal;
   
   ----------
   -- Less --
   ----------
   
   function Less
    (Left  : League.Strings.Universal_String;
     Right : League.Strings.Universal_String) return Boolean is
   begin
      return Left.Collation < Right.Collation;
   end Less;
   
   Binary    : Binary_Order_Sets.Set;
   Collation : Collation_Order_Sets.Set;
   
   S1 : League.Strings.Universal_String
     := League.Strings.To_Universal_String ("ель");
   S2 : League.Strings.Universal_String
     := League.Strings.To_Universal_String ("ёж");
   S3 : League.Strings.Universal_String
     := League.Strings.To_Universal_String ("жук");
   
begin
   Binary.Insert (S1);
   Binary.Insert (S2);
   Binary.Insert (S3);

   Collation.Insert (S1);
   Collation.Insert (S2);
   Collation.Insert (S3);
   
   Ada.Wide_Wide_Text_IO.Put_Line ("Binary order:");
   
   declare
      Position : Binary_Order_Sets.Cursor := Binary.First;
     
   begin
      while Binary_Order_Sets.Has_Element (Position) loop
         Ada.Wide_Wide_Text_IO.Put_Line
          ("  "
             & Binary_Order_Sets.Element (Position).To_Wide_Wide_String);
         Binary_Order_Sets.Next (Position);
      end loop;
   end;
   
   Ada.Wide_Wide_Text_IO.Put_Line ("Collation order:");
   
   declare
      Position : Collation_Order_Sets.Cursor := Collation.First;
     
   begin
      while Collation_Order_Sets.Has_Element (Position) loop
         Ada.Wide_Wide_Text_IO.Put_Line
          ("  "
             & Collation_Order_Sets.Element
                (Position).To_Wide_Wide_String);
         Collation_Order_Sets.Next (Position);
      end loop;
   end;
end Collation;

Вывод примера будет следующим:

Binary order:
  ель
  жук
  ёж
Collation order:
  ёж
  ель
  жук

Преобразование регистра символов

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

  • преобразование в заглавные буквы — To_Uppercase;
  • преобразование в строчные буквы — To_Lowercase;
  • преобразование в буквы, используемые в заголовках — To_Titlecase.

Строго говоря, имеется ещё одна подпрограмма преобразования регистра символов — To_Casefold — выполняющая «сворачивание» регистра символов. Её основное применение — сравнение строк без учёта регистра символов.

И небольшой пример, показывающий использование и результат работы подпрограмм преобразования регистра:


          

          
with Ada.Wide_Wide_Text_IO;

with League.Strings;

procedure Case_Conversion is
   S : League.Strings.Universal_String
     := League.Strings.To_Universal_String ("Ель");
   
begin
   Ada.Wide_Wide_Text_IO.Put_Line (S.To_Wide_Wide_String);
   Ada.Wide_Wide_Text_IO.Put_Line (S.To_Uppercase.To_Wide_Wide_String);
   Ada.Wide_Wide_Text_IO.Put_Line (S.To_Lowercase.To_Wide_Wide_String);
   Ada.Wide_Wide_Text_IO.Put_Line (S.To_Casefold.To_Wide_Wide_String);
end Case_Conversion;

Нормализация

Спецификация Unicode вводит понятия нормализации текстовых данных и четыре формы нормализации. Матрёшка предоставляет четыре подпрограммы для выполнения нормализации в соответствующую нормальную форму — To_NFC, To_NFD, To_NFKC, To_NFKD. Применение нормализации регламентируется различными прикладными спецификациями, поэтому ограничимся только упоминанием наличия необходимых подпрограмм.


Автор: Вадим Годунко
Дата: 18.08.2011