Пример 4.10 разбивает строку с помощью простого алгоритма. Начиная с начала строки, он ищет первое вхождение разделителя с, а затем считает, что все, что стоит после начала строки или предыдущего найденного вхождения и до этого вхождения, является очередным фрагментом текста. Для поиска первого вхождения символа в оригинальной строке string
пример использует метод find
, а для копирования символов диапазона в новую string
, помещаемую в vector
, — метод substr
. Это тот же самый принцип, который используется в функциях разбиения строк большинства скриптовых языков и является специальным случаем
Разделение строки, использующей единственный символ-разделитель, является очень распространенной задачей, и неудивительно, что ее решение есть в библиотеке Boost String Algorithms. Оно просто в использовании. Чтобы увидеть, как разделить строку с помощью функции split
из Boost, посмотрите на пример 4.11.
#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 по адресу 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
, выполняющий эту задачу.
#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;
}