16
17 bool caseInsCompare(const string& s1, const string& s2) {
18 return((s1.size() == s2.size()) &&
19 equal(s1.begin(), s1.end(), s2.begin(), caseInsCharCompareN));
20 }
21
22 bool caseInsCompare(const wstring& s1, const wstring& s2) {
23 return((s1.size() == s2.size())
24 equal(s1.begin(), s1.end(), s2.begin(), caseInsCharCompareW));
25 }
26
27 int main() {
28 string s1 = 'In the BEGINNING...';
29 string s2 = 'In the beginning...';
30 wstring ws1 = L'The END';
31 wstring ws2 = L'the end';
32
33 if (caseInsCompare(s1, s2))
34 cout << 'Equal!
';
35
36 if (caseInsCompare(ws1, ws2))
37 cout << 'Equal!
';
38 }
Критической частью сравнения строк без учета регистра является проверка равенства каждой соответствующей пары символов, так что давайте начнем обсуждение с него. Так как я в этом подходе использую стандартный алгоритм equal
, но хочу использовать свой особый критерий сравнения, я должен создать отдельную функцию, выполняющую это сравнение.
Строки 9-15 примера 4.21 определяют функции, которые выполняют сравнение — caseInsCharCompareN
и caseInsCharCompareW
. Они для преобразования символов к верхнему регистру используют toupper
и towupper
, а затем сообщают, равны ли они.
После написания этих функций сравнения настает время использовать стандартный алгоритм, выполняющий применение этих функций сравнения к произвольной последовательности символов. Именно это делают функции caseInsCompare
, определенные в строках 17-25 и использующие equal
. Здесь сделано две перегрузки — по одной для каждого типа интересующих нас символов. Они обе делают одно и то же, но каждая использует для своего типа символов соответствующую функцию сравнения. Для этого примера я перегрузил две обычные функции, но этот же эффект может быть достигнут и с помощью шаблонов. Для пояснений обратитесь к врезке «Следует ли использовать шаблон?».
equal
сравнивает две последовательности на равенство. Имеется две версии: одна использует operator==
, а другая использует переданный ей функциональный объект двоичного предиката (т.е. такой, который принимает два аргумента и возвращает bool
). В примере 4.21 caseInsCharCompareN
и W
— это функции двоичного предиката.
Но это не всё, что требуется сделать; также требуется сравнить размеры. Рассмотрим объявление equal
.
template<typename InputIterator1, typename InputIterator2,
typename BinaryPredicate>
bool equal(InputIterator1 first, InputIterator1 last1,
InputIterator2 first2, BinaryPredicate pred);
Пусть first1
и last1
, или, другими словами, длина первого диапазона. equal
возвращает true
, если первые n
элементов обеих последовательностей равны. Это означает, что если есть две последовательности, где первые n
элементов равны, но вторая содержит больше чем n
элементов, то equal
вернет true
. Чтобы избежать такой ошибки требуется проверять размер.
Эту логику не обязательно инкапсулировать в функцию. Ваш или клиентский код может просто вызвать алгоритм напрямую, но проще запомнить и написать такое:
if (caseInsCompare(s1, s2)) { // они равны, делаем что-нибудь
чем такое:
if ((s1.size() == s2.size()) &&
std::equal(s1.begin(), s1.end(s2.begin(), caseInsCharCompare<char>)) {
// они равны, делаем что-нибудь
когда требуется выполнить сравнение строк без учета регистра.
4.14. Выполнение поиска строк без учета регистра
Требуется найти в строке подстроку, не учитывая разницу в регистре.
Используйте стандартные алгоритмы transform
и search
, определенные в <algorithm>
, а также свои собственные функции сравнения символов, аналогичные уже показанным. Пример 4.22 показывает, как это делается.
#include <string>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <cctype>
using namespace std;
inline bool caseInsCharCompSingle(char a. char b) {
return(toupper(a) == b);
}
string::const_iterator caseInsFind(string& s, const string& p) {
string tmp;
transform(p.begin( ), p.end(), // Преобразуем шаблон
back_inserter(tmp), // к верхнему регистру
toupper);
return(search(s.begin(), s.end(), // Возвращаем итератор.
tmp.begin(), tmp.end(), // возвращаемый из
caseInsCharCompSingle)); // search
}