Д. Стефенс - C++. Сборник рецептов
- Название:C++. Сборник рецептов
- Автор:
- Жанр:
- Издательство:КУДИЦ-ПРЕСС
- Год:2007
- Город:Москва
- ISBN:5-91136-030-6
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Д. Стефенс - C++. Сборник рецептов краткое содержание
Данная книга написана экспертами по C++ и содержит готовые рецепты решения каждодневных задач для программистов на С++. Один из авторов является создателем библиотеки Boost Iostreams и нескольких других библиотек C++ с открытым исходным кодом. В книге затрагивается множество тем, вот лишь некоторые из них: работа с датой и временем; потоковый ввод/вывод; обработка исключений; работа с классами и объектами; сборка приложений; синтаксический анализ XML-документов; программирование математических задач. Читатель сможет использовать готовые решения, а сэкономленное время и усилия направить на решение конкретных задач.
C++. Сборник рецептов - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Пример 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_);
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, конечно, хорошо, но не все так просто. Предположим, что, вместо того чтобы просто разделить строку на основе единственного разделителя, требуется разбить строку на лексемы . Наиболее частым вариантом этой задачи является разделение на лексемы с игнорированием пробелов. Пример 4.12 дает реализацию класса StringTokenizer
(аналогичного стандартному классу Java™ с таким же именем) для C++, который принимает символы-разделители, но по умолчанию использует пробелы.
Наиболее важные строки в StringTokenizer
используют методы find_first_of
и find_first_not_of
шаблона класса basic_string
. Их описание и примеры использования даны в рецепте 4.9. Пример 4.12 дает такой вывод.
Здесь содержится 3 лексемы.
token = razzle
token = dazzle
token = giddyu
p
StringTokenizer
— это более гибкая форма функции split
из примера 4.10. Он поддерживает свое состояние, так что можно просто последовательно переходить с одной лексемы на другую, не разбивая вначале всю строку на части. Также есть возможность подсчитать число лексем.
В StringTokenizer
можно внести пару усовершенствований. Во-первых, для простоты StringTokenizer
написан так, что он работает только с простыми строками — другими словами, строками из узких символов. Если требуется, чтобы один и тот же класс работал как с узкими, так и с широкими символами, параметризуйте тип символов, как это сделано в предыдущих рецептах. Другим улучшением является расширение StringTokenizer
так, чтобы он обеспечивал более дружественное взаимодействие с последовательностями и был более гибок. Вы всегда можете сделать это сами, а можете использовать имеющийся класс разбиения на лексемы. Проект Boost содержит класс tokenizer
, делающий все это. За подробностями обратитесь к www.boost.org .
Рецепт 4.24.
4.8. Объединение нескольких строк
Имея последовательность строк, такую как вывод примера 4.10, вам требуется объединить их в одну длинную строку, возможно, с разделителями.
В цикле переберите всю последовательность строк и добавьте каждую из них в выходную строку. В качестве входа можно обрабатывать любую стандартную последовательность. Пример 4.13 использует vector
из элементов типа string
.
Пример 4.13. Объединение последовательности строк
#include
#include
#include
using namespace std;
void join(const vector& v, char c, string& s) {
s.clear();
for (vector::const_iterator p = v.begin();
p ! = v.end(); ++p) {
s += *p;
if (p != v.end() - 1) s += c;
}
}
int main() {
vector v;
vector v2;
string s;
v.push_back(string("fее"));
v.push_back(string("fi"));
v.push_back(string("foe"));
v.push_back(string("fum"));
join(v, '/', s);
cout << s << '\n';
}
Пример 4.13 содержит одну методику, которая несколько отличается от предыдущие примеров. Посмотрите на эту строку.
for (vector::const_iterator p = v.begin();
Предыдущие примеры работы со строками использовали iterator
'ы без части «const», но здесь без этого не обойтись, так как v
объявлен как ссылка на объект const
. Если имеется объект контейнера const
, то для доступа к его элементам можно использовать только const_iterator
. Это так потому, что простой iterator
позволяет записывать в объект, на который он указывает, что, конечно, нельзя делать в случае с объектами контейнера типа const
.
v
объявлен как const
по двум причинам. Во-первых, я знаю, что я не собираюсь изменять его содержимое, так что я хочу, чтобы компилятор выдал сообщение об ошибке, если это произойдет. Компилятор гораздо лучше меня в деле поиска таких вещей, особенно когда к такому присвоению приводит тонкая семантическая или синтаксическая ошибка. Во-вторых, я хочу показать пользователям этой функции, что я ничего не делаю с их контейнером, и const
— это великолепный способ сделать это. Теперь я просто должен создать обобщенную версию, которая работает с различными типами символов.
Как и в рецепте 4.6, превращение join
в общий шаблон функции очень просто. Все, что требуется сделать, — это изменить заголовок, параметризовав тип символов, как здесь:
template
void join(const std::vector >& v, T c,
std::basic_string& s)
Но vector
может оказаться не единственным возможным входом функции. Вам может потребоваться объединить строки в стиле С. Класс string
C++ предпочтительнее строк в стиле С, так что если возникает такая задача, объединяйте их в C++ string
. После этого всегда можно получить версию С, вызвав метод string c_str
, который возвращает указатель const
на завершающийся нулем массив символов.
Пример 4.14 предлагает общую версию join
, которая объединяет массив символов в string
. Так как новая общая версия параметризована по типу символов, она будет работать как для массивов узких, так и для массивов широких символов.
Интервал:
Закладка: