возвращают реверсивные итераторы. Реверсивные итераторы ведут себя так, как будто они просматривают последовательность в обратном порядке.
rbegin
возвращает итератор, который указывает на последний элемент, a
rend
возвращает итератор, указывающий на позицию перед первым элементом. Это в точности обратно тому, что делают
begin
и
end
.
Но должны
ли вы обращать строку? С помощью
rbegin
и
rend
для обратной строки можно использовать все методы или алгоритмы, работающие с диапазонами итераторов. А если требуется выполнить поиск в строке, то можно использовать
rfind
, которая делает то же, что и
find
, но начинает с конца строки и движется к ее началу. Для больших строк или большого количества строк обращение может оказаться очень дорогостоящим, так что при возможности избегайте его.
4.6. Разделение строки
Проблема
Требуется разделить строку с разделителями на несколько строк. Например, может потребоваться разделить строку
"Name|Address|Phone"
на три отдельных строки —
"Name"
,
"Address"
и
"Phone"
, удалив при этом разделитель.
Решение
Для перехода от одного вхождения разделителя к следующему используйте метод
find
класса
basic_string
, а для копирования каждой подстроки используйте
substr
. Для хранения результатов используйте любую стандартную последовательность. Пример 4.10 использует
string s = "Account Name|Address 1|Address 2 |City";
split(s, '|', v);
for (int i = 0; i < v.size; ++i) {
cout << v[i] << '\n';
}
}
Обсуждение
Превращение приведенного выше примера в шаблон функции, принимающий любой тип символов, тривиально — просто параметризуйте тип символов и
замените случаи использования
string
на
basic_string<T>
.
template<typename T>
void split(const basic_string<T>& s, T c,
vector<basic_string<T> >& v) {
basic_string<T>::size_type i = 0;
basic_string<T>::size_type j = s.find(c);
while (j != basic_string<T>::npos) {
v.push_back(s.substr(i, j-i));
i = ++j;
j = s.find(c, j);
if (j == basic_string<T>::npos)
v.push back(s.substr(i, s.length));
}
}
Логика при этом не меняется.
Однако обратите внимание, что между двумя последними угловыми скобками в последней строке заголовка функции добавлен один пробел. Это требуется для того, чтобы сказать компилятору, что это не оператор сдвига вправо.
Пример 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;