Глава 3
Вверх
Подумайте, а что собственно представляет "регистр" символа? В иврите, фарси и японской письменности отсутствуют концепции верхнего и нижнего регистров, поэтому для этих языков такая концепция бессмысленна. Конечно можно создать механизм пометки некоторых языков "только верхним" или "только нижним" регистром, это позволит содать обобщенное решение.
Но в ряде языков, поддерживающих концепцию регистра, смысл некоторых символов с диакритическими знаками меняется при изменении регистра (например седиль в испанском, кумфлекс во французском или умляут в немецком языке). По этой причине любые схемы с регистровыми символами, претендующие на полноту и законченность, окажутся неимоверно сложными в использовании.
Хотя строки С++ обычно интерпретируются как классы, на самом деле это не совсем так. Тип string представляет собой специализацию более общего шаблона basic_string. Посмотрите как выглядит объявление string в стандартном заголовочном файле С++:
typedef basic_string<char> string;
Чтобы лучше понять природу класса string, стоит взглянуть на шаблон basic_string:
template<class charT, class traits = char_traits<charT>,
В главе 5 шаблоны будут рассматриваться более подробно. А пока обратите внимание, что тип string создается специализацией шаблона basic_string по типу char. Внутри объявления basic_string<> следующая строка сообщает, что поведение класса, созданного на базе шаблона basic_string, задается классом, созданным на базе шаблона char_traits<>:
class traits = char_traits<charT>
Таким образом на базе шаблона basic_string<>, создаются строковые классы, которые помимо char могут работать с другими типами (например с символами в расширенной кодировке). При этом шаблон char_traits<> определяет порядок сортировки в различных кодировках при помощи функций eq() (равно), ne() (не равно), lt() (меньше). От этих функций зависит работа функций сравнения строк basic_string<>.
Теперь понятно, почему класс string не содержит функций, не учитывающих регистр символов: это не входит в его задачи. Чтобы изменить способ сравнения строк в классе string, следует предоставить другой шаблон char_traits<>, поскольку именно он определяет поведение отдельных функций сравнения символов.
На основе этой инф можно создать новую разновидность класса string игнорирующую регистр символов при сравнениях. Сначала мы должны определить новый шаблон char_traits<>,
производный от существующего шаблона, который бы игнорировал регистр символов.
Затем следует переопределить только те функции, которые бы обеспечивали посимвольное сравнение без учета регистра (кроме тех функций лексического сравнения, о которых упоминалось ранее, так же определяется новая реализация функций find() и compare() шаблона char_traits<>).
Наконец мы определяем новую специализацию на базе шаблона basic_string<>, но передаем во втором аргументе новый шаблон char_traits<>:
Вверх
using std::allocator;
struct ichar_traits : char_traits<char> {
static const char*
find(const char* s1, size_t n, char c) {
typedef basic_string<char, ichar_traits> istring;
inline ostream& operator<<(ostream& os, const istring& s) {
Пример:
int main() {
Результат:
tHis
Анализ:
АВ:Как мы видим сравнения без учета регистров символов прошли успешно!
Впрочем это сильно упрощенный, "ненастоящий" пример. Чтобы класс istring стал полностью эквивалентным string, необходимо определить другие функции, обеспечивающие его работу.
Посмотрим функцию сравнения строк, без учета регистра символов:
Как видим функции передаются три параметра :
указатель на первый символьный массив (строка 1)
Здесь в этой функции строки рассматриваются как символьные массивы. И сравнение строк
будет происходить посимвольно.
Назад |
Начало урока |
Вверх |
Вперед
Строки и характеристики символов
Знакомясь с программой Find.cpp, приведенной ранее в этой главе трудно удержаться от очевидного вопроса: почему сравнение без учета регистра символов не поддерживается в стандартном классе string? Ответ заставляет по-новому взглянуть на истинную природу строковых объектов С++.
class allocator = allocator<charT> > class basic_string
//: C03:ichar_traits.h
// Создание пользовательских классов характеристик символов
#ifndef ICHAR_TRAITS_H
#define ICHAR_TRAITS_H
#include <cassert>
#include <cctype>
#include <cmath>
#include <cstddef>
#include <ostream>
#include <string>
using std::basic_string;
using std::char_traits;
using std::ostream;
using std::size_t;
using std::string;
using std::toupper;
using std::tolower;
static bool eq(char c1st, char c2nd) {
static bool ne(char c1st, char c2nd) {
static bool lt(char c1st, char c2nd) {
static int
compare(const char* str1, const char* str2, size_t n) {
str1++; str2++; // Compare the other chars
return 0;
Мы создали определение типа istring, чтобы наш класс был во всех отношениях аналогичен обычному классу string, кроме одного - все сравнения осуществляются без учета регистра символов. Для удобства так же предоставлена перегруженная версия операторной функции operator<<() для вывода строк.
//: C03:ICompare.cpp
#include <cassert>
#include <iostream>
#include "ichar_traits.h"
using namespace std;
istring first = "tHis";
istring second = "ThIS";
cout << first << endl;
cout << second << endl;
assert(first.compare(second) == 0);
assert(first.find('h') == 1);
assert(first.find('I') == 2);
assert(first.find('x') == string::npos);
ThIS
static int
compare(const char* str1, const char* str2, size_t n) {
str1++; str2++; // Compare the other chars
return 0;
указатель на второй символьный массив (строка 2)
переменная n, равная надо полагать длине наибольшего символьного массива
Содержание