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;
}
return (count_ = n);
}
bool hasMoreTokens() { return(begin_ != end_); }
void nextToken(string& s) {
if (begin_ != string::npos && end_ != string::npos) {
s = str_.substr(begin_, end_-begin_);
begin_ = str_.find_first_not_of(delim_, end_);
end_ = str_.find_first_of(delim_, begin_);
} else if (begin_ != string::npos && end_ == string::npos) {
s = str_.substr(begin_, str_.length()-begin_);
begin_ = str_.find_first_not_of(delim_, end_);
}
}
private:
StringTokenizer() {}
string delim_;
string str_;
int count_;
int begin_;
int end_;
};
int main() {
string s = " razzle dazzle giddyup ";
string tmp;
StringTokenizer st(s);
cout << "Здесь содержится" << st.countTokens() << " лексемы.\n";
while (st.hasMoreTokens()) {
st.nextToken(tmp);
cout << "token = " << trap << '\n';
}
}
Разбиение строки с четко определенной структурой, как в примере 4.10, конечно, хорошо, но не все так просто. Предположим, что, вместо того чтобы просто разделить строку на основе единственного разделителя, требуется StringTokenizer (аналогичного стандартному классу Java™ с таким же именем) для C++, который принимает символы-разделители, но по умолчанию использует пробелы.
Наиболее важные строки в StringTokenizer используют методы find_first_of и find_first_not_of шаблона класса basic_string. Их описание и примеры использования даны в рецепте 4.9. Пример 4.12 дает такой вывод.
Здесь содержится 3 лексемы.
token = razzle
token = dazzle
token = giddyup
StringTokenizer — это более гибкая форма функции split из примера 4.10. Он поддерживает свое состояние, так что можно просто последовательно переходить с одной лексемы на другую, не разбивая вначале всю строку на части. Также есть возможность подсчитать число лексем.
В StringTokenizer можно внести пару усовершенствований. Во-первых, для простоты StringTokenizer написан так, что он работает только с простыми строками — другими словами, строками из узких символов. Если требуется, чтобы один и тот же класс работал как с узкими, так и с широкими символами, параметризуйте тип символов, как это сделано в предыдущих рецептах. Другим улучшением является расширение StringTokenizer так, чтобы он обеспечивал более дружественное взаимодействие с последовательностями и был более гибок. Вы всегда можете сделать это сами, а можете использовать имеющийся класс разбиения на лексемы. Проект Boost содержит класс tokenizer, делающий все это. За подробностями обратитесь к
Рецепт 4.24.
4.8. Объединение нескольких строк
Имея последовательность строк, такую как вывод примера 4.10, вам требуется объединить их в одну длинную строку, возможно, с разделителями.
В цикле переберите всю последовательность строк и добавьте каждую из них в выходную строку. В качестве входа можно обрабатывать любую стандартную последовательность. Пример 4.13 использует vector из элементов типа string.
#include
#include
#include
using namespace std;
void join(const vector
s.clear();
for (vector
p ! = v.end(); ++p) {
s += *p;