На этом шаге мы рассмотрим более подробно понятие интернационализации.
Как уже упоминалось во вводном описании строковых классов, шаблон строкового класса basic_string<> параметризуется по типу символов, трактовкам типа символов и модели памяти. Тип string представляет собой специализированную версию шаблона для символов типа char, а тип wstring предназначен для символов типа wchar_t.
Трактовки типа символов представляют собой информацию о том, как следует поступать в зависимости от представления типа символов. Необходимость в дополнительном классе объясняется тем, что интерфейс встроенных типов (таких, как char и wchar_t) изменять нельзя, но один символьный тип может трактоваться по-разному.
В следующем фрагменте определяется специальный класс трактовок для строк, благодаря которому операции со строками выполняются без учета регистра символов:
#ifndef ICSTRING_HPP #define ICSTRING_HPP #include <string> #include <iostream> #include <cctype> // Замена функций стандартного класса char_traits<char> // для того, чтобы операции со строками // выполнялись без учета регистра символов struct ignorecase_traits : public std::char_traits<char> { // Проверка равенства c1 и c2 static bool eq(const char& c1, const char& c2) { return std::toupper(c1)==std::toupper(c2); } // Проверка условия "с1 меньше c2" static bool lt(const char& c1, const char& c2) { return std::toupper(c1)<std::toupper(c2); } // Сравнение до n символов s1 и s2 static int compare(const char* s1, const char* s2, std::size_t n) { for (std::size_t i=0; i<n; ++i) { if (!eq(s1[i],s2[i])) { return lt(s1[i],s2[i])?-1:1; } } return 0; } // Поиск c в s static const char* find(const char* s, std::size_t n, const char& c) { for (std::size_t i=0; i<n; ++i) { if (eq(s[i],c)) { return &(s[i]); } } return 0; } }; // Определение специального типа для таких строк typedef std::basic_string<char,ignorecase_traits> icstring; /* Определение оператора вывода, * так как тип трактовок отличен от типа, * заданного для std::ostream */ inline std::ostream& operator << (std::ostream& strm, const icstring& s) { // Простое преобразование icstring в обычную строку return strm << std::string(s.data(),s.length()); } #endif // ICSTRING_HPP
Определение оператора вывода необходимо из-за того, что стандарт описывает операторы ввода-вывода только для потоков данных, у которых тип символов соответствует типу трактовок. В данном случае мы используем другой тип трактовок, поэтому для него приходится определять собственный оператор вывода. Аналогичная проблема существует и для операторов ввода.
В следующей программе показано, как использовать специализированный вид строк:
//--------------------------------------------------------------------------- #include <vcl.h> #include "icstring.hpp" #include <conio.h> //необходимо для getch() #pragma hdrstop //--------------------------------------------------------------------------- #pragma argsused using namespace std; std::string ToRus(const std::string &in) { char *buff = new char [in.length()+1]; CharToOem(in.c_str(),buff); std::string out(buff); delete [] buff; return out; } int main (int argc, char* argv[]) { using std::cout; using std::endl; icstring s1("hallo"); icstring s2("otto"); icstring s3("hALLo"); cout << std::boolalpha; cout << s1 << " == " << s2 << " : " << (s1==s2) << endl; cout << s1 << " == " << s3 << " : " << (s1==s3) << endl; icstring::size_type idx = s1.find("All"); if (idx != icstring::npos) { cout << ToRus("Номер вхождения строки \"All\" в строку \"") << s1 << "\": " << idx << endl; } else { cout << ToRus("Строка \"All\" отсутствует в строке \"") << s1 << endl; } getch(); return 0; } //---------------------------------------------------------------------------
Результат выполнения программы выглядит так:
Рис.1. Результат работы приложения
На следующем шаге мы остановимся более подробно на эффективности.