Пример 4.10 разбивает строку с помощью простого алгоритма. Начиная с начала строки, он ищет первое вхождение разделителя с, а затем считает, что все, что стоит после начала строки или предыдущего найденного вхождения и до этого вхождения, является очередным фрагментом текста. Для поиска первого вхождения символа в оригинальной строке string пример использует метод find, а для копирования символов диапазона в новую string, помещаемую в vector, — метод substr. Это тот же самый принцип, который используется в функциях разбиения строк большинства скриптовых языков и является специальным случаем разделения строки текста на лексемы (tokenizing), описываемого в рецепте 4.7.

Разделение строки, использующей единственный символ-разделитель, является очень распространенной задачей, и неудивительно, что ее решение есть в библиотеке Boost String Algorithms. Оно просто в использовании. Чтобы увидеть, как разделить строку с помощью функции split из Boost, посмотрите на пример 4.11.

Пример 4.11. Разделение строки с помощью Boost

#include <iostream>

#include <string>

#include <list>

#include <boost/algorithm/string.hpp>

using namespace std;

using namespace boost;

int main() {

 string s = 'one,two,three,four';

 list<string> results;

 split(results, s, is_any_of(',')); // Обратите внимание - это boost::split

 for (list<string>::const_iterator p = results.begin();

  p != results.end(); ++p) {

  cout << *p << endl;

 }

}

split — это шаблон функции, принимающий три аргумента. Он объявлен вот так.

template<typename Seq, typename Coll, typename Pred>

Seq& split(Seq& s, Coll& c, Pred p,

 token_compress_mode_type e = token_compress_off);

Seq, Coll и Pred представляют типы результирующей последовательности, входной коллекции и предиката, используемого для определения, является ли очередной объект разделителем. Аргумент последовательности — это последовательность, определенная по стандарту C++, содержащая нечто, что может хранить части того, что находится во входной коллекции. Так, например, в примере 4.11 был использован list<string>, но вместо него можно было бы использовать и vector<string>. Аргумент коллекции — это тип входной последовательности. Коллекция — это нестандартная концепция, которая похожа на последовательность, но с несколько меньшими требованиями (за подробностями обратитесь к документации по Boost по адресу www.boost.org). Аргумент предиката — это объект унарной функции или указатель на функцию, которая возвращает bool, указывающий, является ли ее аргумент разделителем или нет. Она вызывается для каждого элемента последовательности в виде f(*it), где it — это итератор, указывающий на элемент последовательности.

is_any_of — это удобный шаблон функции, поставляющийся в составе String Algorithms, которая облегчает жизнь при использовании нескольких разделителей. Он конструирует объект унарной функции, которая возвращает true, если переданный ей аргумент является членом набора. Другими словами:

bool b = is_any_of('abc')('a'); // b = true

Это облегчает проверку нескольких разделителей, не требуя самостоятельного написания объекта функции.

4.7. Разбиение строки на лексемы

Проблема

Требуется разбить строку на части, используя набор разделителей.

Решение

Для перебора элементов строки и поиска места нахождения следующих лексем и не-лексем используйте методы find_first_of и first_first_not_of. Пример 4.12 представляет простой класс StringTokenizer, выполняющий эту задачу.

Пример 4.12. Разбиение строки на лексемы

#include <string>

#include <iostream>

using namespace std;

// Класс, разбивающий строку на лексемы.

class StringTokenizer {

public:

 StringTokenizer(const string& s, const char* delim = NULL) :

  str_(s), count(-1), begin_(0), end_(0) {

  if (!delim)

   delim_ = ' f v'; //по умолчанию пробельные символы

  else

   delim_ = delim;

  // Указывает на первую лексему

  begin_ = str_.find_first_not_of(delim);

  end_ = str.find_first_of(delim_, begin_);

 }

 size_t countTokens() {

  if (count_ >= 0) // если уже посчитали, то выход

   return(count_);

  string::size_type n = 0;

  string::size_type i = 0;

  for (;;) {

   // переход на первую лексему

   if ((i = str_.find_first_not_of(delim_, i)) == string::npos)

    break;

   // переход на следующий разделитель

   i = str_.find_first_of(delim_, i+1);

   n++;

   if (i == string::npos) break;

  }

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату