Выбрать главу

Эту логику не обязательно инкапсулировать в функцию. Ваш или клиентский код может просто вызвать алгоритм напрямую, но проще запомнить и написать такое:

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 показывает, как это делается.

Пример 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

}

int main() {

 string s = "row, row, row, your boat";

 string p = "YOUR";

 string::const_iterator ir = caseInsFind(s, p);

 if (it != s.end()) {

  cout << "Нашли!\n;

 }

}

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

Обсуждение

Пример 4.22 демонстрирует обычный ход действий при работе со стандартными алгоритмами. Создайте функцию, которая выполняет работу, а затем подключите ее как объект функции к наиболее подходящему алгоритму. Здесь работу выполняет функция charInsCharCompSingle, но в отличие от примера 4.21 эта функция сравнения символов переводит к верхнему регистру только первый аргумент. Это сделано потому, что немного далее в caseInsFind я перед использованием строки шаблона в поиске преобразую ее к верхнему регистру полностью и тем самым избегаю многократного преобразования символов строки шаблона к верхнему регистру.

После того как функция сравнения будет готова, используйте для поиска стандартные алгоритмы transform и search. transform используется для преобразования к верхнему регистру всего шаблона (но не целевой строки). После этого используйте для поиска места вхождения подстроки search совместно с функцией сравнения.

Помните, что стандартные алгоритмы работают с последовательностями, а не со строками. Это общие алгоритмы, которые в основном (но не только) работают со стандартными контейнерами, но не делают никаких предположений о содержимом этих контейнеров. Все стандартные алгоритмы требуют передачи им функции сравнения (а в случае их отсутствия используют операторы по умолчанию), которые тем или иным способом сравнивают два элемента и возвращают bool, указывающий, дало ли сравнение истину или ложь.

В примере 4.22 есть одна вещь, которая выглядит странно. Вы видите, что caseInsCompare возвращает const_iterator, как в

string::const_iterator caseInsFind(const string& s,

 const string& p)

Что, если требуется изменить элемент, на который указывает возвращенный итератор? Тому есть причина. Она состоит в том, что константный итератор используется потому, что строки, которые передаются в caseInsFind, также передаются как const, и, следовательно, невозможно получить не-const итератор на const-строку. Если требуется итератор, который можно использовать для изменения строки, удалите const из параметров и измените объявление функции так, чтобы она возвращала string::iterator.

4.15. Преобразование между табуляциями и пробелами в текстовых файлах

Проблема

Имеется текстовый файл, содержащий табуляции или пробелы, и требуется преобразовать одни в другие. Например, может потребоваться заменить все табуляции на последовательности из трех пробелов или сделать наоборот и заменить все вхождения некоторого числа пробелов на табуляции.

Решение

Независимо от того, производится ли замена табуляций на пробелы или пробелов на табуляции, используйте классы ifstream и ofstream из <fstream>. В первом (более простом) случае прочтите данные по одному символу с помощью входного потока, изучите их и, если очередной символ — это табуляция, запишите в выходной поток некоторое количество пробелов. Пример 4.23 демонстрирует, как это делается.

Пример 4.23. Замена табуляций на пробелы

#include <iostream>

#include <fstream>

#include <cstdlib>

using namespace std;

int main(int argc, char** argv) {

 if (argc < 3)

  return(EXIT_FAILURE);

 ifstream in(argv[1]);

 ofstream out(argv[2]);

 if (!in || !out) return(EXIT_FAILURE);

 char c;

 while (in.get(c)) {

  if (c == '\t')

   out << " "; // 3 пробела

  else

   out << c;

 }

 out.close();

 if (out)

  return(EXIT_SUCCESS);

 else

  return(EXIT_FAILURE);

}

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

Пример 4.24. Замена пробелов на табуляции

#include <iostream>

#include <istream>

#include <ostream>

#include <fstream>

#include <cstdlib>