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

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

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

Мэтт Остерн

Если вам когда-либо доводилось писать программы, в которых используются строки (а кому, спрашивается, не доводилось?), скорее всего, вы встречались с типичной ситуацией — две строки, различающиеся только регистром символов, должны были интерпретироваться как равные. В этих случаях требовалось, чтобы операции сравнения — проверка равенства, больше-меньше, выделение подстрок, сортировка — игнорировали регистр символов. Программисты очень часто спрашивают, как организовать подобные операции средствами стандартной библиотеки C++. На этот вопрос существует огромное количество ответов, многие из которых неверны.

Прежде всего необходимо избавиться от мысли о написании класса, сравнивающего строки без учета регистра. Да, с технической точки зрения это более или менее возможно. Тип std::string стандартной библиотеки в действительности является синонимом для типа std::basic_string<char, std::char_trais<char>, std::allocator<char> >. Операции сравнения определяются вторым параметром; передавая второй параметр с переопределенными операциями «равно» и «меньше», можно специализировать basic_string таким образом, что операции < и = будут игнорировать регистр символов. Такое решение возможно, но игра не стоит свеч.

• Вы не сможете выполнять операции ввода-вывода или это потребует больших дополнительных хлопот. Классы ввода-вывода стандартной библиотеки (такие как std::basic_istream и std::basic_ostream) специализируются по двум начальным параметрам std::basic_stringstd::ostream всего лишь является синонимом для std::basic_ostream<char, char_traits<char> >). Параметры характеристик (traits) должны совпадать. Если вы используете строки типа std::basic_string<char, my_traits_class>, то для вывода строк должен использоваться тип std::basic_ostream<char, my_traits_class>. Стандартные потоки cin и cout для этой цели не подойдут.

• Игнорирование регистра символов является не свойством объекта, а лишь контекстом его использования. Вполне возможно, что в одном контексте строки должны интерпретироваться с учетом регистра, а в другом контексте регистр должен игнорироваться (например, при установке соответствующего режима пользователем).

• Решение не соответствует канонам. Класс char_traits, как и все классы характеристик[5], прост, компактен и не содержит информации состояния. Как будет показано ниже, правильная реализация сравнений без учета регистра не отвечает ни одному из этих критериев.

• Этого вообще не достаточно. Даже если все функции basic_string будут игнорировать регистр, это никак не отразится на использовании внешних обобщенных алгоритмов, таких как std::search и std::find_end. Кроме того, такое решение перестает работать, если по соображениям эффективности перейти от контейнера объектов basic_string к таблице строк.

Более правильное решение, которое лучше соответствует архитектуре стандартной библиотеки, заключается в том, чтобы игнорировать регистр символов только в тех случаях, когда это действительно необходимо. Не стоит возиться с такими функциями контейнера string, как string::find_first или string::rfind; они лишь дублируют функциональные возможности, уже поддерживаемые внешними обобщенными алгоритмами. С другой стороны, алгоритмы обладают достаточной гибкостью, что позволяет реализовать в них поддержку сравнений строк без учета регистра. Например, чтобы отсортировать коллекцию строк без учета регистра, достаточно передать алгоритму працильный объект функции сравнения:

std::sort(C.begin(), C.end().compare_without_case);

Написанию таких объектов и посвящена эта статья.

Оглавление книги


Генерация: 1.708. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз