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

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

Жанры

Эффективное использование 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 секунды.

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

Черный дембель. Часть 5

Федин Андрей Анатольевич
5. Черный дембель
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Черный дембель. Часть 5

30 сребреников

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

Жребий некроманта 2

Решетов Евгений Валерьевич
2. Жребий некроманта
Фантастика:
боевая фантастика
6.87
рейтинг книги
Жребий некроманта 2

Охота на разведенку

Зайцева Мария
Любовные романы:
современные любовные романы
эро литература
6.76
рейтинг книги
Охота на разведенку

Чужбина

Седой Василий
2. Дворянская кровь
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Чужбина

Возвышение Меркурия. Книга 4

Кронос Александр
4. Меркурий
Фантастика:
героическая фантастика
боевая фантастика
попаданцы
5.00
рейтинг книги
Возвышение Меркурия. Книга 4

Надуй щеки! Том 3

Вишневский Сергей Викторович
3. Чеболь за партой
Фантастика:
попаданцы
дорама
5.00
рейтинг книги
Надуй щеки! Том 3

Идеальный мир для Лекаря 16

Сапфир Олег
16. Лекарь
Фантастика:
боевая фантастика
юмористическая фантастика
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 16

По воле короля

Леви Кира
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
По воле короля

Он тебя не любит(?)

Тоцка Тала
Любовные романы:
современные любовные романы
7.46
рейтинг книги
Он тебя не любит(?)

Курсант: назад в СССР 9

Дамиров Рафаэль
9. Курсант
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Курсант: назад в СССР 9

Штуцер и тесак

Дроздов Анатолий Федорович
1. Штуцер и тесак
Фантастика:
боевая фантастика
альтернативная история
8.78
рейтинг книги
Штуцер и тесак

Камень Книга седьмая

Минин Станислав
7. Камень
Фантастика:
фэнтези
боевая фантастика
6.22
рейтинг книги
Камень Книга седьмая

Хозяйка дома в «Гиблых Пределах»

Нова Юлия
Любовные романы:
любовно-фантастические романы
5.75
рейтинг книги
Хозяйка дома в «Гиблых Пределах»