Как-то на RSDN я прочитал, что Boost "драматически" ускоряет разработку. Вот-таки да. Для целого ряда рутинных и не очень задач библиотека Boost просто незаменима. Скажем, вроде бы такая простая задача, как сравнение строк без учета регистров, в С++ вырастает в настоящую проблему: необходимо сначала определить новый std::char_traits, выполняющий сравнение строк без учета регистра, а затем создать на его базе новый класс istring, экземпляры которого будут сравниваться без учета регистра. Угу, а если те же строки надо сравнить с учетом регистра? Это что преобразовывать снова в std::string? В общем, аут полный. Можно еще воспользоваться одной из функций стандартной библиотеки, принимающими char *, завершающимися символом NULL. Стоит ли напоминать, что экземпляры string не обязаны завершаться на NULL, и могут содержать несколько этих символов. В общем, более-менее универсальное решение видится только в использовании функции std::equals и нужного функтора. Ну, а если нужно распрасить CSV-файл? Тоже приятного мало. Поиск запятых и кавычек после этого будет сниться во сне. А затем еще необходимо конвертировать строки в int и double, а вдруг эти строки содержат ведущие или хвостовые пробелы? Ох, грехи мои тяжкие. Конечно, эти задачи решаются в C++, но достаточно муторно все это, а в том же C# существуют функции String.Trim() и Convert.ToDouble().
Что есть Boost
Итак, что такое библиотека Boost. Ее история начинается с того времени, когда принимали стандарт С++. В какой-то момент, для форсирования принятия стандарта, было принято решение не включать в С++ новых возможностей (разгрести бы то, что уже добавили). В качестве к дополнению к стандарту был организован набор библиотек Boost (не оставлять же в свободном плавании превосходные библиотеки). Принципы добавления новых библиотек в Boost довольно просты: сообщество Boost должно одобрить идею (ее необходимость и полезность) новой библиотеки и реализацию оной. Дело это довольно небыстрое, сообщество общается посредством новостных групп.
Библиотека (ну, или набор библиотек – что не суть важно) Boost расширяет и дополняет STL.
Скачать Boost вы можете по адресу: http://www.boost.org/. Там же есть и инструкции по установке (см. Getting Started).
Примеры использования Boost
Перебор элементов коллекции
Для доступа к элементам коллекции библиотека STL предлагает использовать итераторы.
В следующем примере, просто создается и заполняется vector<int>, а затем все элементы вектора выводятся на консоль:
#include <vector>
#include <iostream>
void stl_foreach()
{
std::vector<int> lstInt;
for(int i = 0; i < 10; ++i)
{
lstInt.push_back(i);
}
std::vector<int>::iterator pos = lstInt.begin();
for(; pos != lstInt.end(); ++pos)
{
std::cout << *pos << std::endl;
}
}
|
Макрос BOOST_FOREACH предлагает абстрагироваться от концепции итераторов и перебрать элементы коллекции в стиле оператора foreach C#:
#include <vector>
#include <iostream>
#include <boost/foreach.hpp>
void boost_foreach()
{
std::vector<int> lstInt;
for(int i = 0; i < 10; ++i)
{
lstInt.push_back(i);
}
BOOST_FOREACH(int i, lstInt)
{
std::cout << i << std::endl;
}
}
|
В ряде случаев такой подход более удобен и просто сокращает время на набор. В общем, не самый бесполезный макрос.
Форматированный вывод
Вновь вернемся к C#. Тем, кто знаком с .NET не понаслышке, наверняка знакомы методы Console.WriteLine() и String.Format(). Позволяющие создавать форматированный вывод, да и просто создавать форматированные строки:
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("{0}+{1}={2}", 5, 6, 5+6);
}
}
|
Библиотека Boost предлагает класс boost::format с аналогичными возможностями форматирования:
#include <iostream>
#include <boost/format.hpp>
void format1()
{
std::cout << boost::format("%1%+%2%=%3%") % 5 % 6 % (5+6) << std::endl;
}
|
Более сложный пример форматирования:
void format2()
{
std::cout << boost::format("%|1$7|+%|2$7|=%|3$+7|") % 5 % 6 % (5+6) << std::endl;
std::cout << boost::format("%|7|+%|7|=%|+7|") % 5 % 6 % (5+6) << std::endl;
}
|
Обе строки выводят:
Также возможно форматирование в стиле printf:
void format3()
{
std::cout << boost::format("%7d+%7d=%+7d") % 5 % 6 % (5+6) << std::endl;
std::cout << boost::format("%1$7d+%2$7d=%3$+7d") % 5 % 6 % (5+6) << std::endl;
}
|
Вывод такой же как в предыдущем примере.
Для преобразования объекта boost::format в std::string предназначена функция boost::str:
void format4()
{
boost::format formatter("%1%+%2%=%3%");
std::string str = boost::str(formatter % 5 % 6 % (5+6));
std::cout << str << std::endl;
}
|
В целом, boost::format весьма гибкое и мощное средство. И для более полного использования его возможностей я рекомендую изучить документацию по нему.
Строковые операции.
Библиотека Boost string_algo предлагает не самый маленький набор строковых операций, среди них поиск вхождения подстрок, в том числе с использованием регулярных выражение, операции сравнения с учетом и без учета регистра, обрезка ведущих и хвостовых пробелов и много других операций. Для использования библиотеки необходимо включить заголовочный файл boost/algorithm/string.hpp. К сожалению, строковые алгоритмы этой библиотеки находятся в пространстве имен boost::algorithm – поэтому без использования директивы using не обойтись. В следующем примере рассматриваются функции сравнения строк с учетом регистра (boost::algorithm::equals) и без учета регистра (boost::algorithm::iequals), а также функция, обрезающая хвостовые и ведущие пробелы (boost::algoritm::trim) – для удобства используется ее аналог boost::algorithm::trim_copy, который вместо модифицирования строки, возвращает измененную копию строки:
#include <string>
#include <iostream>
#include <boost/algorithm/string.hpp>
void string1()
{
using boost::algorithm::equals;
using boost::algorithm::iequals;
using boost::algorithm::trim_copy;
using std::cout;
using std::endl;
std::string str_ABC = "ABC";
std::string str_abc = "abc";
cout << std::boolalpha;
cout << equals(str_ABC, str_abc) << endl;
cout << iequals(str_ABC, str_abc) << endl;
std::string str_abc_spaces(" abc ");
cout << iequals(str_ABC, str_abc_spaces) << endl;
cout << iequals(str_ABC, trim_copy(str_abc_spaces)) << endl;
}
|
Конвертирование строк
Задача конвертирования строк в double, int и т.п. встречается достаточно часто. Библиотека Boost предлагает для этих целей шаблон lexical_cast<>. Использование его элементарно и вряд ли вызовет какие-либо вопросы:
#include <iostream>
#include <boost/lexical_cast.hpp>
void cast1()
{
using boost::lexical_cast;
unsigned k = lexical_cast<unsigned>("101");
std::cout << k << std::endl;
double db = lexical_cast<double>("12.3");
std::cout << db << std::endl;
try
{
float h = lexical_cast<float>("xy12");
}
catch(boost::bad_lexical_cast &)
{
std::cerr << "Unable to convert \"xy12\" to float" << std::endl;
}
}
|
Вывод функции следующий:
101
12.3
Unable to convert "xy12" to float
|
Как вы уже догадались в случае неудачи boost::lexical_cast<> генерит исключение boost::bad_lexical_cast.
Разбор текста
А напоследок самый интересный пример, парсинг CSV-файла с помощью boost::tokenizer<>. Как видно из названия boost::tokenizer разбивает переданную строку на токены, при этом разделители задаются с помощью первого параметра шаблона. В нашем случае мы будет использовать предопреленный класс boost::escaped_list_separator<>, предназначенный для разбора текста в формате CSV. Для задания иных наборов разделителей обратите внимание на boost::char_separator<>.
Итак, у нас есть CSV-файл test.csv с несколькими записями:
1,"Andrey",200.5
4,"Michael",300 78,"Peter",12.567
|
Задача: распарсить файл и вывести результат на консоль.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/tokenizer.hpp>
void csv_parser(const std::string &file_in)
{
typedef boost::tokenizer<boost::escaped_list_separator<char> > CsvTokenizer;
using boost::lexical_cast;
using boost::algorithm::trim_copy;
struct Record
{
unsigned Index;
std::string Name;
double Value;
};
std::string line;
std::ifstream ifs(file_in.c_str());
std::vector<Record> recs;
while(getline(ifs, line))
{
CsvTokenizer token(line);
CsvTokenizer::iterator pos = token.begin();
Record rec;
rec.Index = lexical_cast<unsigned>(trim_copy(*pos));
++pos;
rec.Name = *pos;
++pos;
rec.Value = lexical_cast<double>(trim_copy(*pos));
recs.push_back(rec);
}
BOOST_FOREACH(const Record &rec, recs)
{
std::cout << boost::format("%1%, %2%, %3%") % rec.Index % rec.Name % rec.Value << std::endl;
}
}
|
В приведенном выше коде tokenizer получает очередную строку CSV-файла и парсит ее (CsvTokenizer token(line)), а затем возвращает итератор (CsvTokenizer::iterator pos = token.begin()) перечисляющий токены распарсенной строки. Все остальное рассматривалось выше и, думаю, не требует объяснений.
Заключение
Приведенные выше несколько примеров позволят оценить полезность буста и научиться использовать часть его библиотек. Это неплохо для начала использования библиотек Boost, но никак не позволяет претендовать на звание эксперта в нем :-). Boost поставляется вместе с очень неплохой документацией по нему, поэтому недостающие знания можно будет легко восполнить при необходимости.
Александр Игнатьев
http://www.vestace.ru/
18.05.2008