Рейтинговые книги
Читем онлайн C++. Сборник рецептов - Д. Стефенс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 43 44 45 46 47 48 49 50 51 ... 136

  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>

using namespace std;

void spacesToTabs(istream& in, ostream& out, int spaceLimit) {

 int consecSpaces = 0;

 char c;

 while (in.get(c)) {

  if (c != ' ') {

   if (consecSpaces > 0) {

    for (int i = 0; i < consecSpaces; i++) {

     out.put(' ');

    }

    consecSpaces = 0;

   }

   out.put(c);

  } else {

   if (++consecSpaces == spaceLimit) {

    out.put('t');

    consecSpaces = 0;

   }

  }

 }

}

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);

 spacesToTabs(in, out, 3);

 out.сlose();

 if (out)

  return(EXIT_SUCCESS);

 else

  return(EXIT_FAILURE);

}

Обсуждение

Механизм обоих этих решений один и тот же, отличаются только алгоритмы. Символы читаются из входного потока с помощью get, а в выходной поток помещаются с помощью put. Логика, выполняющая преобразования, помещается между этими двумя функциями.

Вы, вероятно, заметили в примере 4.24, что в функции main in и out объявлены как переменные типов ifstream и ofstream соответственно и что параметры spacesToTabs — это istream и ostream. Это сделано для того, чтобы позволить spacesToTabs работать с любыми типами входных и выходных потоков (ну, не любыми типами потоков, а теми, которые наследуются от basic_istream или basic_ostream), а не только с файловыми потоками. Например, текст, который требуется переформатировать, может находиться в строковом потоке (istringstream и ostringstream из <sstream>). В этом случае сделайте что-то похожее на следующее.

istringstream istr;

ostringstream ostr;

// заполняем istr текстом...

spacesToTabs(istr, ostr);

Как и в случае со строками, потоки — это на самом деле шаблоны классов, параметризованные по типу символов, с которыми работает поток. Например, ifstream — это typedef для basic_ifstream<char>, a wifstream — это typedef для basic_ifstream<wchar_t>. Таким образом, если требуется, чтобы spacesToTabs из примеров 4.23 или 4.24 работала с потоками любых символов, то вместо typedef используйте эти шаблоны классов.

template<typename T>

void spacesToTabs(std::basic_istream<T>& in,

 std::basic_ostream<T>& out, int spaceLimit) { //...

4.16. Перенос строк в текстовом файле

Проблема

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

Решение

Напишите функцию, которая использует входной и выходной потоки, читает символы с помощью istream::get(char), выполняет какие-либо действия и записывает символы с помощью ostream::put(char). Пример 4.25 показывает, как это делается с файлом, который содержит обычный текст, с учетом сохранения целостности слов.

Пример 4.25. Перенос текста

#include <iostream>

#include <fstream>

#include <cstdlib>

#include <string>

#include <cctype>

#include <functional>

using namespace std;

void textWrap(istream& in, ostream& out, size_t width) {

 string tmp;

 char cur = '';

 char last = '';

 size_t i = 0;

 while (in.get(cur)) {

  if (++i == width) {

   ltrimws(tmp);       // ltrim как в рецепте

1 ... 43 44 45 46 47 48 49 50 51 ... 136
На этой странице вы можете бесплатно читать книгу C++. Сборник рецептов - Д. Стефенс бесплатно.

Оставить комментарий