Чтение онлайн

на главную - закладки

Жанры

Эффективное использование STL
Шрифт:

Фасет collate

Если вы знакомы с локальными контекстами C++, возможно, вам уже пришел в голову другой способ сравнения строк. У фасета

collate
, предназначенного для инкапсуляции технических аспектов сортировки, имеется функция, по интерфейсу весьма близкая к библиотечной функции C
strcmp
. Существует даже специальное средство, упрощающее сравнение двух строк: для объекта локального контекста
L
строки 
x
и 
y
могут сравниваться простой записью
L(x, y)
, что позволяет обойтись без хлопот, связанных с вызовом
use_facet
и функции
collate
.

«Классический»

локальный контекст содержит фасет
collate
, который выполняет лексикографическое сравнение по аналогии с функцией
operator<
контейнера
string
, но другие локальные контексты выполняют сравнение, руководствуясь своими критериями. Если в системе существует локальный контекст, обеспечивающий сравнение строк без учета регистра для интересующих вас языков, воспользуйтесь им. Возможно, этот локальный контекст даже не будет ограничиваться простым сравнением отдельных символов!

К сожалению, какой бы справедливой ни была эта рекомендация, она никак не поможет тем, у кого нет таких локальных контекстов. Возможно, когда-нибудь в будущем стандартное множество таких локальных контекстов будет стандартизировано, но сейчас никаких стандартов не существует. Если функция сравнения без учета регистра для вашей системы еще не написана, вам придется сделать это самостоятельно.

Сравнение строк без учета регистра

Фасет

ctype
позволяет относительно легко организовать сравнение строк без учета регистра на основе сравнения отдельных символов. Приведенная ниже версия не оптимальна, но по крайней мере она верна. В ней используется практически тот же принцип, что и прежде: строки сравниваются алгоритмом
lexicographical_compare
, а отдельные символы сравниваются после приведения к верхнему регистру. Впрочем, на этот раз вместо глобальной переменной используется объект локального контекста. Кстати говоря, сравнение после приведения обоих символов к верхнему регистру не всегда дает тот же результат, что и после приведения к нижнему регистру. Например, во французском языке в символах верхнего регистра принято опускать диакритические знаки, вследствие чего вызов
toupper
во французском локальном контексте может приводить к потере информации: символы 'ё' и 'е' преобразуются в один символ верхнего регистра 'Е'. В этом случае при сравнении на базе функции
toupper
символы ''e' и 'е' будут считаться одинаковыми, а при сравнении на базе
tolower
они будут считаться разными. Какой из ответов правилен? Вероятно, второй, но на самом деле все зависит от языка, национальных обычаев и специфики приложения.

struct lt_str_1:

 public std::binary_function<std::string, std::string, bool> {

 struct lt_char {

const std::ctype<char>& ct;

 lt_char(const std::ctype<char>& c):ct(c) {}

 bool operator(char x, char y) const {

return ct.toupper(x) < ct.toupper(y);

 }

};

std::locale loc;

const std::ctype<char>& ct;

lt_str_l(const std::locale& L = std::locale::classic):

 loc(L), ct(std::use_facet<std::ctype<char> >(loc)) {}

bool operator(const std::string& x, const std::string& y) const {

 return std::lexicographical_compare(x.begin, x.end,

y.begin, y.end, lt_char(ct));

 }

};

Данное

решение не оптимально; оно работает медленнее, чем могло бы работать. Проблема чисто техническая: функция
toupper
вызывается в цикле, а Стандарт C++ требует, чтобы эта функция была виртуальной. Некоторые оптимизаторы выводят вызов виртуальной функции из цикла, но чаще этого не происходит. Циклические вызовы виртуальных функций нежелательны.

В данном случае тривиального решения не существует. Возникает соблазнительная мысль — воспользоваться одной из функций объекта

ctype
:

const char* ctype<char>::toupper(char* f, char* i) const

Эта функция изменяет регистр символов в интервале

[f, i]
. К сожалению, для наших целей этот интерфейс не подходит. Чтобы воспользоваться этой функцией для сравнения двух строк, необходимо скопировать обе строки в буферы и затем преобразовать их содержимое к верхнему регистру. Но откуда возьмутся эти буферы? Они не могут быть массивами фиксированного размера (неизвестно, каким должен быть максимальный размер), а динамические массивы потребуют дорогостоящего выделения памяти.

Альтернативное решение заключается в однократном преобразовании каждого символа с кэшированием результата. Такое решение недостаточно универсально — в частности, при использовании 32-разрядных символов UCS-4 оно абсолютно неработоспособно. С другой стороны, при работе с типом

char
(8-разрядным в большинстве систем) идея хранения 256 байт дополнительных данных в объекте функции сравнения выглядит вполне реально.

struct lt_str_2:

 public std::binary_function<std::string, std::string, bool> {

 struct lt_char {

const char* tab;

lt_char(const char* t) : tab(t) {}

bool operator (char x, char y) const {

return tab[x-CHAR_MIN] < tab[y-CHAR_MIN];

 }

};

char tab[CHAR_MAX-CHAR_MIN+1];

lt_str_2(const std::locale& L = std::locale::classic) {

 const std::ctype<char>& ct = std::use_facet<std::ctype<char> >(L);

 for (int i = CHAR_MIN; i<=CHAR_MAX; ++i) tab[i-CHAR_MIN]=(char)i;

 ct.toupper(tab, tab+(CHAR_MAX-CHAR_MIN+1));

}

bool operator(const std::string& x, const std::string& y) const {

 return std::lexicographical_compare(x.begin, x.end,

y.begin, y.end, lt_char(tab));

 }

}

Как видите, различия между

lt_str_1
и
lt_str_2
не так уж велики. В первом случае используется объект функции сравнения символов, использующий фасет
ctype
напрямую, а во втором случае — объект функции сравнения с таблицей заранее вычисленных преобразований символов к верхнему регистру. Второе решение уступает первому, если создать объект функции
lt_str_2
, воспользоваться им для сравнения нескольких коротких строк и затем уничтожить. С другой стороны, при обработке больших объемов данных
lt_str_2
работает значительно быстрее
lt_str_1
. В моих тестах превосходство было более чем двукратным: при использовании
lt_str_1
сортировка списка из 23 791 слова заняла 0,86 секунды, а при использовании
lt_str_2
понадобилось только 0,4 секунды.

Поделиться:
Популярные книги

Князь Андер Арес 3

Грехов Тимофей
3. Андер Арес
Фантастика:
рпг
аниме
фэнтези
5.00
рейтинг книги
Князь Андер Арес 3

Бестужев. Служба Государевой Безопасности. Книга третья

Измайлов Сергей
3. Граф Бестужев
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Бестужев. Служба Государевой Безопасности. Книга третья

Мастер решений

Земляной Андрей Борисович
3. Специалист по выживанию
Фантастика:
боевая фантастика
космическая фантастика
6.20
рейтинг книги
Мастер решений

Неучтенный элемент. Том 1

NikL
1. Антимаг. Вне системы
Фантастика:
городское фэнтези
фэнтези
5.00
рейтинг книги
Неучтенный элемент. Том 1

Возвращение

Кораблев Родион
5. Другая сторона
Фантастика:
боевая фантастика
6.23
рейтинг книги
Возвращение

Двойник Короля 2

Скабер Артемий
2. Двойник Короля
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Двойник Короля 2

1941, Великая Отечественная катастрофа: Итоги дискуссии

Коллектив авторов
Документальная литература:
военная документалистика
6.25
рейтинг книги
1941, Великая Отечественная катастрофа: Итоги дискуссии

Страж Кодекса. Книга IV

Романов Илья Николаевич
4. КО: Страж Кодекса
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Страж Кодекса. Книга IV

Черная стрела

Стивенсон Роберт Льюис
Приключения:
исторические приключения
8.83
рейтинг книги
Черная стрела

Старый, но крепкий 7

Крынов Макс
7. Культивация без насилия
Фантастика:
рпг
уся
фэнтези
5.00
рейтинг книги
Старый, но крепкий 7

Последний Герой. Том 1

Дамиров Рафаэль
1. Последний герой
Фантастика:
попаданцы
альтернативная история
фантастика: прочее
5.00
рейтинг книги
Последний Герой. Том 1

Мастер...

Чащин Валерий
1. Мастер
Фантастика:
героическая фантастика
попаданцы
аниме
6.50
рейтинг книги
Мастер...

Сильнейший Столп Империи. Книга 2

Ермоленков Алексей
2. Сильнейший Столп Империи
Фантастика:
аниме
фэнтези
фантастика: прочее
попаданцы
5.00
рейтинг книги
Сильнейший Столп Империи. Книга 2

Князь Целитель 2

Ткачев Андрей Юрьевич
2. Князь Целитель
Фантастика:
боевая фантастика
городское фэнтези
аниме
фэнтези
5.00
рейтинг книги
Князь Целитель 2