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

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

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

#include

#include

#include

#include

using namespace std;

using namespace boost;

int main() {

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

 list results;

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

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

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

  cout << *p << endl;

 }

}

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

template

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

 token_compress_mode_type e = token_compress_off);

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

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

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

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

<p>4.7. Разбиение строки на лексемы</p>Проблема

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

Решение

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

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

#include

#include

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\n\r\t\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_);

Перейти на страницу:

Похожие книги