Шаг 528.
Библиотека STL.
Интернационализация. Различия в кодировках символов. Трактовки символов

    На этом шаге мы рассмотрим особенности трактовок символов.

    Различия в кодировках существенны для обработки строк и ввода-вывода. Например, признак "конца файла" и конкретные особенности сравнения символов могут различаться в зависимости от реализации.

    Предполагается, что строковые и потоковые классы будут специализироваться встроенными типами, прежде всего char и wchar_t. Интерфейс встроенных типов должен оставаться неизменным, поэтому информация о разных аспектах представления символов выделяется в отдельный класс - так называемый класс трактовок символов. Он передается строковым и потоковым классам в аргументе шаблона. По умолчанию в этом аргументе передается класс char_traits, параметризованный по аргументу, определяющему тип символов строки или потока данных:

namespace std {
    template<class charT,
             class traits = char_traits<charT>,
             class Allocator = allocator<charT> >
    class basic_string;
}

namespace std {
    template<class charT,
             class traits = char_traits<charT> >
    class basic_istream;
    template<class charT,
             class traits = char_traits<charT> >
    class basic_ostream;
    ...
}

    Трактовки символов указываются в классе char_traits<>, который определяется в заголовочном файле <string> и параметризуется по конкретному типу символов:

namespace std {
  template <class charT> 
  struct char_traits {
  .   .   .   .
  };
}

    Классы трактовок определяют все основные свойства типа символов и операции, необходимые для реализации строк и потоков данных как статических компонентов. Члены класса char_traits перечислены в таблице 1.

Таблица 1. Члены класса трактовок символов
Выражение Описание
char_type Тип символов (то есть аргумент шаблона char_traits)
int_type Тип, размеры которого достаточны для представления дополнительного, не используемого для других целей признака конца файла
pos_type Тип, используемый для представления позиций в потоке
off_type Тип, используемый для представления смещений между позициями в потоке
state_type Тип, используемый для представления текущего состояния в многобайтовых потоках
assign(c1, с2) Присваивает c1 символ с2
eq(c1, с2) Проверяет равенство символов c1 и с2
lt(c1, с2) Проверяет условие "символ c1 меньше символа с2"
length(s) Возвращает длину строки s
compare(s1, s2, n) Сравнивает до n символов строк s1 и s2
copy(s1, s2, n) Копирует n символов строки s2 в s1
move(s1, s2, n) Копирует n символов строки s2 в s1, причем строки s1 и s2 могут перекрываться
assign(s, n, с) Присваивает символ с n символам строки s
find(s, n, с) Возвращает указатель на первый символ строки s, равный с; если среди первых n символов такой символ отсутствует, возвращает 0
eof() Возвращает признак конца файла
to_int_type(c) Преобразует символ с в соответствующее представление типа int_type
to_char_type(i) Преобразует представление i типа int_type в символ (результат преобразования EOF не определен)
not_eof(i) Возвращает значение i, если i не является представлением EOF; в этом случае возвращается значение, определяемое реализацией (и отличное от EOF)
eq_int_type(i1, i2) Проверяет равенство двух символов i1 и i2, представленных в виде типа int_type (иначе говоря, аргументы могут быть равны EOF)

    Функции обработки строк или последовательностей символов присутствуют только по соображениям оптимизации. Они могут быть реализованы при помощи функций, обрабатывающих отдельные символы. Например, функция сору() реализуется на базе assign(), однако при работе со строками возможны более эффективные реализации.

    Все значения количества символов в таблице 1 задаются точно, то есть без учета символов завершения строк.

    Последняя группа функций предназначена для работы с признаком конца файла (EOF). Этот служебный символ расширяет кодировку и требует специальной обработки. Для некоторых представлений типа символов может оказаться недостаточно, поскольку значение символа EOF должно отличаться от значений всех "обычных" символов кодировки. По правилам языка С функции чтения символов возвращали тип int вместо char. В C++ эта методика была усовершенствована. В трактовках символов char_type определяется как тип для представления всех символов, a int_type - как тип для представления всех символов и EOF. Функции to_char_type(), to_int_type(), not_eof() и eq_int_type() определяют соответствующие преобразования и сравнения. Для некоторых классов трактовок типы char_type и int_type могут быть идентичными. Например, если не все значения типа char_type необходимы для представления символов, одно из свободных значений может использоваться для представления символа конца файла.

    Типы pos_type и off_type используются для определения позиций и смещений (подробности смотри на 496 шаге).

    В стандартную библиотеку C++ включены специализации char_traits<> для типов char и wchar_t:

namespace std {
  template<> struct char_traits<char>;
  tempiate<> struct char_traits<wchar_t>; 
}

    Специализация для char обычно реализуется при помощи глобальных строковых функций языка С, определяемых в файлах <cstring> и <string.h>. Примерная реализация может выглядеть так:

namespace std {
    template<> struct char_traits<char> {
      // Определения типов:
      typedef char      char_type;
      typedef int       int_type; 
      typedef streampos pos_type;
      typedef streamoff off_type;
      typedef mbstate_t state_type;

      // Функции:
      static void assign(char& c1, const char& c2) {
          c1 = c2;
      }
      static void eq(const char& c1, const char& c2) {
          return c1 == c2;
      }
      static bool lt(const char& c1, const char& c2) {
          return c1 < c2;
      }
      static size_t length(const char* s) {
          return strlen(s);
      }
      static int compare(const char* s1, const char* s2, size_t n) {
          return memcmp(s1,s2,n);
      }
      static char* copy(char* s1, const char* s2, size_t n) {
          return (char*)memcpy(s1,s2,n);
      }
      static char* move(char* s1, const char* s2, size_t n) {
          return (char*)memmove(s1,s2,n);
      }
      static char* assign(char* s1, size_t n, char c) {
          return (char*)memset(s,c,n);
      }
      static const char* find(const char* s, size_t n,
                              const char& c) {
          return (const char*)memchr(s,c,n);
      }
      static int eof() {
          return EOF;
      }
      static int to_int_type(const char& c) {
          return (int)(insigned char)c;
      }
      static char to_char_type(const int& i) {
          return (char)i;
      }
      static int not_eof(const int& i) {
          return i!=EOF ? i : !EOF;
      }
      static bool eq_int_type(const int& i1, const int& i2) {
          return i1==i2;
      }
    };
}

    Реализация пользовательского класса трактовок, обеспечивающего работу со строками без учета регистра символов, приведена на 386 шаге.

    На следующем шаге мы рассмотрим интернационализацию специальных символов.




Предыдущий шаг Содержание Следующий шаг