《Boost程序完全开发指南》

1.3开发环境
由于Boost大量使用了C++高级特性(如模板偏特化、ADL),因此不是所有的编译器都
能够很好地支持Boost。


在VC集成环境中使用嵌入工程编译的方式需要定义宏BOOST_ALL_NO_LIB或者
BOOST_XXX_NO_LIB(XXX是某个库的名称),以指示BOOST库不要使用自动链接功能。


如果在debug版工程,不要忘记在Preprocessor页中定义宏"_STLP_DEBUG"和"
__STL_DEBUG"以使得STLport。


第2章 时间与日期
2.6处理日期
date_time库的日期基于格里高利历,支持从1400-01-01到9999-12-31之间的日期计算,
它不能处理公元前的日期。


boost.smart_ptr库是对C++98标准的一个绝佳补充。它提供了六种智能指针,包括
scoped_ptr、scoped_array、shared_ptr、shared_array、weak_ptr和intrusive_ptr,
从各文件来增强std::auto_ptr,而且是异常安全的。库中的两个类---shared_ptr和'
weak_ptr已被收入到C++新标准的TR1库中。


它们对于所指的类型T仅有一个很小且很合理的要求:类型T的析构函数不能抛出异常。


这些智能指针都位于名字空间boost,为了使用smart_ptr组件,需要包含头文件
<boost/smart_ptr.hpp>。


根据C++标准,对0(空指针)执行delete操作是安全的。


scoped_ptr的成员get(),它返回scoped_ptr内部保存的原始指针,可以在某些要求必
须是原始指针的场景(如底层的C接口)。但要记住,我们不能对这个指针做delete
并且没有置0的操作,否则scoped_ptr析构时会对已经删除的指针再进行操作。


不能对智能指针对象使用delete操作,因为智能指针是一个行为类似指针的对象,而不
是指针,对一个对象应用delete是不允许的。


scoped_ptr与auto_ptr一样,不能用作容器的元素,但原因不同,auto_ptr是因为它的
转移语义,而scoped_ptr则是因为不支持拷贝和赋值,不符合容器对元素类型的要求。


scoped_ptr与auto_ptr一根本性区别在于指针的所有权。auto_ptr特意被设计为指针的
所有权是可转移的,可以在函数之间传递,同一时刻只能有一个auto_ptr管理指针。
而scoped_ptr把拷贝构造函数和赋值函数都声明为私有的,拒绝了指针所有权的转让--
除了scoped_ptr自己,其他任何人都无权访问被管理的指针,从而保证了指针的绝对
安全。


scoped_array弥补了标准库中没有指向数组的智能指针的缺憾。它包装了new[]操作符。
但不推荐使用scoped_array,而推荐使用vector。


3.4 shared_ptr
shared_ptr是最重要的智能指针。它使用引用计数。它可以安全地放到标准容器中。弥补
了auto_ptr因为转移语义而不能把指针作为STL容器元素的缺陷。
shared_ptr的reset()函数的行为与scoped_ptr也不尽相同,它的作用是将引用计数减1
停止对指针的共享,除非引用计数为0,否则不会发生删除操作。带参数的reset()则
类似相同形式的构造函数,原指针引用计数减1的同时改为管理另一个指针。


unique()在shared_ptr是指针的唯一所有者时返回true。use_count()返回当前指针的
引用计数,但use_count()要小心使用,它应该仅仅用于测试或者调试,它不提供高效率
的操作,而且有时候可能不可用的(极少数情形)。而unique()则是可靠的,任何时候
都可用,而且比use_count() == 1速度更快。


shared_ptr还支持相等或不相等,以及operator<比较大小,且都是基于内部保存的指
针。这使用得shared_ptr可以被用于标准关联容器(set和map)。shared_ptr很好地消除
了显式的delete调用。


shared_ptr在头文件<boost/make_shared.hpp>中提供了一个自由工厂函数
make_shared<T>(),来消除显式的new调用,它的名字提供了标准库的make_pair(),
声明如下:
template<class T, class... Args>
shared_ptr<T> make_shared(Args &&... args);


shared_ptr<string> sp = make_shared<string>("make_shared");
shared_ptr<vector<int> > spv = make_shared<vector<int> >(10, 2);


3.4.5 应用于标准容器
有两种方式可以将shared_ptr应用于标准容器(或者容器适配器等其他容器)。
一种用法是将容器作为shared_ptr管理的对象,如shared_ptr<list<T> >,使用容器可
以被安全地共享,用法与普通shared_ptr没有区别。
另一种用法是将shared_ptr作为容器的元素,如vector<shared_ptr<T> >。


记住标准容器不能容纳auto_ptr和scoped_ptr等


3.4.6 应用于桥接模式
桥接模式bridge是一种结构型设计模式,它把类的具体实现细节对用户隐藏起一类。在
具体编程实践中桥接模式也被称为pimpl或者handle/body惯用法。它可以将头文件的
依赖关系降到最小,减少编译时间,而助可以不使用虚函数实现多态。它可以任意改变
具体的实现而外界对此一无所知,也减少了源文件之间的编译依赖。


3.4.8 定制删除器
shared_ptr(Y* p, D d),它涉及shared_ptr的另一个重要概念:删除器。用d来删除,
即把delete p换成d(p)。删除器d可以是函数对象或函数指针,使用得d(p)成立。


shared_ptr提供一个自由函数get_deleter(shared_ptr<T> const&p),它能够返回删除
器的指针。
有了删除器的概念,我们就可以用shared_ptr实现管理任意资源。只要这种资源提供了
它自己的释放操作,shared_ptr就能够保证自动释放。


3.9 pool
pool只能作为普通数据类型如int、double等的内存池,不能应用于复杂的类和对象,因
为它只分配内存,不调用构造函数,这个时候,我们需要使用object_pool。




第4章 实用工具
4.1 noncopyable
4.2 typeof
4.3 optional
optional库使用“容器”语义,包装了“可能产生无效值”的对象,实现了“未初始化”
的概念。
optional很像一个仅能存放一个元素的容器,它实现了“未初始化”的概念:如果元素
未初始化,那么容器就是空的,否则,容器内就是有效的、已初始化的值。
optional的比较是深比较。


4.3.5 工厂函数
optional提供一个类似make_pair()、make_shared()的工厂函数make_optional()。
可以根据参数类型自动推导optional的类型,用来辅助创建optional对象。


optional<T>要求类型T具有拷贝语义,因为它内部会保存值的拷贝。


4.4 assign
STL容器仅提供了容纳这些数据的方法,但填充的步骤却是相当地麻烦,必须重复调用
insert()或push_back()等成员函数,这正是boost.assign出现的理由。


assign库重载了赋值操作符operator+=、逗号操作符operator,和括号操作符operator()


使用assign库时必须使用using指示符,只有这样才能让重载的+=,等操作符在作用域内
生效,如:
#include <boost/assigh.hpp>
int main()
{
using namespace boost::assign; //很重要,启用assign库的功能
vector<int> v;
v += 1,2,3,4,6*6;


set<string> s;
s += "cpp", "java", "python";


map<int, string> m;
m += make_pair(1, "one"), make_pair(2, "two");
}


+=操作符后可以接若干个可被容器容纳的元素,元素之间使用逗号分隔。元素不一定是
常量,表达式或者函数调用也是可以接受的,只要其结果能够转换成容器可容纳的类型。
比较特别的是map容器,必须使用make_pair()辅助函数来生成容器元素。


operator+=很好用,但有一点遗憾,它仅限应用于STL中定义的标准容器,vector,list,
set等,对于其他类型的容器(如boost新容器)则无能为力。


4.4.2 使用操作符()向容器增加元素
assign库还提供了三个辅助函数insert()、push_front()、push_back()。这些函数可
作用于拥有同名成员函数的容器,接受容器变量作为参数,返回一个代理对象
list_inserter。如
vector<int> v;
push_back(v)(1)(2)(3);
map<int, string> m;
insert(m)(1, "one")(2, "two"); // 对于set和map,只能使用assign::insert(),如果
括号中没有参数,则将调用容器元素的缺省构造函数填入一个缺省值,逗号操作符则不
能这样做。


括号操作符也可以与逗号等操作符配合使用,写法更简单,有时甚至看起来不像是合法
的C++代码,例如:
using namespace boost::assign;
vector<int> v;
push_back(v), 1, 2, 3, 4;
push_back(v)(6), 7, 64/8, (9), 10;
//v = [6,7,8,9,10];
set<string> d;
insert(d)() = "cpp", "java", "python";
//d = ["", "cpp", "java", "python"]


4.4.3 初始化容器元素
assign库使用list_of()、map_list_of()/pair_list_of()和tuple_list_of()三个函数
解决了在容器构造的时候就完成数据的填充。


4.4.4 减少重复输入
assign库提供repeat()、repeat_fun()和range()三个函数来减轻工作量。


vector<int> v = list_of(1).repeat(3,2)(3)(4)(5);
//v = 1,2,2,2,3,4,5;
multiset<int> ms;
insert(ms).repeat_fun(5, &read).repeat(2,1),10;
//ms = x,x,x,x,x,1,1,10;
deque<int> d;
push_front(d).range(v.begin(), v.begin()+5);
//d = 3,2,2,2,1


4.4.5 与非标准容器工作
assign库不仅支持全部八个STL标准容器vector,string, deque,list, set, multiset,
map, multimap,也对STL中的容器适配器提供了适当的支持,包括stack,queue和
priority_queue.
因为stackt等容器适配器不符合容器的定义,没有insert、push_back等成员函数,所
以不能使用赋值的方式填入元素,只能使用初始化的方式。并在list_of表达式最后
使用to_adapter()成员函数来适配到非标准容器。如果使用逗号操作符还需要把整个
表达式用括号括起来,才能使用点号调用to_adapter()。
queue<string> q = (list_of("china")("us")("uk")).repeat(2, "russia").
to_adapter();
assign库也支持部分不在STL中定义的非标准容器,如STLport中的slist和hash_set/
hash_map,因为它们符合容器的定义,故用法与标准容器没有什么区别。


此外,assign库还支持大部分Boost库容器,如array, circular_buffer,unordered等。


4.4.6 高级用法
list_of()嵌套使用
list_of()可以就地创建匿名列表,这一点很有用,它可以嵌套在assign库用法中。
例如,下面的代码使用vector构造了了个二维数组,使用list_of(list_of())的嵌套
形式来初始化:
vector<vector<int> > v = list_of(list_of(1)(2))(list_of(3)(4));


v += list_of(5)(6), list_of(7)(8);


4.5 swap
boost::swap是对标准库提供的std::swap的增强和泛化。
为交换两个变量(可以是int等内置数据类型,或者是类实例、容器)的值提供了便捷
的方法。


std::swap()要求交换的对象必须是可拷贝构造和可拷贝赋值的,即重载operator=。但
这种交换是最通用但也是效率最低的方法。


解决方案有两种:一是直接利用函数重载; 二是使用ADL(argument dependent lookup,
参数依赖查找)查找模板特化的swap。这两种方案就是boost::swap的工作原理。


它查找有无针对类型T的std::swap()的特化swap()或者通过ADL查找模板特化的swap(),
如果有则调用,如果两种查找都失败时则退化为std::swap()。此外,boost::swap还
增加了对C++内建数组交换的支持(它要求两个数组大小相同)。


如果读者担心在全局或std名字空间编写自由函数swap会造成名字“污染”,也可以把特化
的swap加入到boost名字空间,或者其他ADL可以找到的名字空间。


4.5.5 使用建议
我们应该尽量使用boost::swap,它提供了比std::swap更好的优化策略。并且你自己写
的类应该总是实现在高效的交换。或者想交换两个数组的内容。


变量值交换是一个基础但很重要的操作,几乎所有boost库组件都实现了自己的swap
成员函数,并且用boost::swap来提高交换的效率,


4.6 singleton
目前boost中并没有专门的单件库,而仅在其他库中有并不十分完善的实现。


4.6.1 boost.pool的单件实现
#include <boost/pool/detail/singleton.hpp>
using boost::details::pool::singleton_default; //单件名字空间
class point
{
public:
point(int a = 0, int b = 0, int c = 0):x(a), y(b), z(c)
{cout<<"point ctor"<<endl;}
~point()
{cout<<"point dtor"<<endl;}
...
};


int main()
{
cout<<"main() start"<<endl;
typedef singleton_default<point> origin; // 定义单件类
origin::instance().print(); // 使用instance()获得单件对象
cout<<"main() finish"<<endl;
}
singleton_default用法非常简单,只需要把想成为单件的类作为它的模板参数就可以了
唯一的不方便之处就是过长的类型名,但可以用typedef来简化。


4.6.2 boost.serialization的单件实现
在序列化库serialization中有另一个单件实现类:singleton,它们于名字空间
boost::serialization。需要包含头文件<boost/serialization/singleton.hpp>


singleton与pool.singleton_default基本相同,对模板参数T也有同样的要求(具有
缺省构造函数,构造析构不抛异常)。


4.7 tribool
在处理tribool的不确定状态时必须要小心,因为它既不是true也不是false,使用它
进行条件判断永远都不会成立,判断不确定状态必须要与indeterminate值比较或者
使用indeterminate()函数。


4.7.3 tribool库默认采用indeterminate作为第三态的名字,很清晰明确但可能有些长。
tribool库使用宏BOOST_TRIBOOL_THIRD_STATE就可以为第三态更名。像这样:
BOOST_TRIBOOL_THIRD_STATE(unknown)。


因为宏BOOST_TRIBOOL_THIRD_STATE实质上定义了一个函数,而C++不允许函数嵌套,所
以这个宏最好在全局域使用,它将在定义后的整个源代码中都生效。


4.7.4 输入输出,tribool需要包含头文件<boost/logic/tribool_io.hpp>就可以像
bool类型一样进行流操作了。


4.8 operators
在C++98标准的std::rel_ops名字空间里提供了四个模板比较操作符!=, >, <=, >=
只需要为类定义了==和<操作符,那么这四个操作符就可以自动实现了。
例如:
#include <utility>
class demo_class
{
public:
demo_class(int n):x(n){}
int x;
friend bool operator<(const demo_class& l, const demo_class&r)
{return l.x < r.x;}
};


int main()
{
demo_class a(10), b(20);
using namespace std::rel_ops; //打开std::rel_ops名字空间
cout<<(a<b)<<endl;
cout<<(b>=a)<<endl; //>=等操作符被自动实现
}


operators位于名字空间boost,为了使用operators组件,需要包含头文件<boost/
operators.hpp>,


4.8.1 基本运算概念
operators库是由多个类组成,分别用来实现不同的运算概念,比如less_than_compparable
定义了<系列操作符,left_shiftable定义了<<系列操作符。


我们可以使用多重继承来获得多个操作符的重载。


4.8.3 基类链
4.8.5 相等与等价
相等equality与等价equivalent是两个极易被混淆的概念。一个简单的解释是:相等是
基于操作符==,即x==y;而等价基于<,即!(x<y)&&!(x>y)。
但对于大多数复杂类型和自定义类型,==和<操作符是两个不同的运算。
如p1(1,2,3)和p3(3,2,1)两者完全不相等,但等价。


标准库中的关联容器set,map和排序算法使用的是等价关系<操作符,而各种查找算法
find使用的是相等关系的==操作符。


4.8.6 解引用操作符
operators库使用dereferenceable提供了对解引用操作符*,->的支持,它的用法与之前
介绍的算术操作符不太相同。


dereferenceable类要求子类提供operator*,会自动实现operator->。与前面的算术
操作符不同的是它不是使用的友元函数,因此在使用dereferenceable时必须使用
public继承,否则operator->将会成为类的私有成员函数,外界无法访问。


4.8.7 下标操作符
operators库使用indexable提供下标操作符[]的支持,它也属于解引用的范畴。用法与
dereferenceable很相似。
indexable要求子类提供一个operator+(T, I)的操作定义,类似于一个指针的算术运算


4.9 exception
4.9.1标准库中的异常
为了使用boost.exception,我们需要先了解C++98标准规定的异常体系。
C++98标准中定义了一个异常基类std::exception和try/catch/throw异常处理机制,
std::exception又派生出若干子类。用以描述不同种类的异常,如bad_alloc,
bad_cast,out_of_range等等,共同构建了C++异常处理框架。


C++允许任何类型作为异常抛出,但在std::exception出现后,我们应该尽量使用它,因
为std::exception提供了一个很有用的成员函数what(),可以返回异常所携带的信息,
这比简单地抛出一个整数错误值或者字符串更好、更安全。


如果std::exception及其子类不能满足程序对异常处理的要求,我们也可以继承它,为
它添加更多的异常诊断信息。


4.9.2类摘要
exception库提供两个类:exception和error_info,它们是exception库的基础。


exception是一个抽象类,它的重要能力在于其友元操作符<<,可以存储error_info对象
的信息,存入的信息可以用自由函数get_error_info<>()随时再取出来。


应该总对异常类使用虚继承。这是由于异常的特殊处理机制决定的。从现在开始,对
boost::exception应该总使用虚继承。


因为exception被定义为抽象类,因此我们的程序必须定义它的子类才能使用它,如前
所述exception必须使用虚继承的方式。通常,继承完成后自定义异常类的实现也就结
束了,不需要再添加成员变量或成员函数,这些工作都已经由exception完成了。
如:
struct my_exception:
virtual std::exception, // 虚继承
virtual boost::exception
{}; //空实现,不需要实现代码


4.10 uuid
uuid库是一个小的实用工具,可以表示和生成UUID.
UUID是Universally Unique Identifier的缩写,它是一个128位的数字(16字节),不
需要有一个中央认证机构就可以创建全球唯一的标识符
UUID的另一个别名是GUID,在微软的COM中被广泛使用,用于标识COM组件接口。


4.10.2用法
uuid是一个很小的类,它特意被设计为没有构造函数,可以像POD数据类型一样使用。
uuid内部使用一个16字节的数组data作为uuid值的存储,这个数组是public的,因此
可以任意访问,比如拷贝或者赋值。
可以把uuid看作是一个容量固定为16、元素类型为unsigned char的容器。


UUID的生成有不同的算法,这些算法使用枚举version_type来标识,version()函数可以
获得UUID的算法版本。uuid类可以识别现有的五种生成算法,分别是:
一、基于时间的MAC的算法 version_time_based
二、分布计算环境算法 dce_security
三、MD5摘要算法 version_name_based_md5
四、随机数算法 version_random_number_based
五、SHA1摘要算法 version_name_based_sha1


在数量宠大的UUID中有一个特殊的全零值nil,它表示一个无效的UUID,成员函数
is_nil()可以检测uuid是否是nil。


4.10.3 生成器
uuid库提供了四种生成器,分别是Nil生成器、字符串生成器、名字生成器和随机生成
器。它们都是函数对象,重载了operator(),可以直接调用生成uuid对象。


Nil生成器
只能生成一个无效的UUID值(即全零的UUID).
Nil生成器的类名是nil_generator,另外有一个内联函数nil_uuid(),相当于直接调用
了Nil生成器。
如:
uuid u = nil_generator()();
asseert(u.is_nil());


u = nil_uuid();
assert(u.is_nil());
注意,代码的第一行,在nil_generator类名后面出现了两对圆括号,不熟悉函数对象
的读者可能会不太理解。它的语义解析是:第一对圆括号与nil_generator结合,结果
是调用nil_generator的构造函数,生成一个临时对象,然后第二对圆括号是
nil_generator对象的operator()操作符重载,就像是一个函数调用,产生了一个
nil uuid对象。


字符串生成器
string_generator


名字生成器
name_generator使用基于名字的SHA1摘要算法。它需要先指定一个基准UUID,然后使用
字符串名字派生出基于这个UUID的一系列UUID。名字生成器的典型的应用场景是为一个
组织内的所有成员创建UUID标识,只要基准UUID不变,那么相同的名字总会产生相同的
UUID。
例如:
uuid www_xxx_com = string_generator()("0123456789abcdef0123456789abcdef");
name_generator ngen(www_xxx_com);


uuid u1 = ngen("mario");


uuid u2 = ngen("link");


随机数生成器
random_generator rgen;
uuid u = rgen();


4.10.4 增强的uuid类
uuid类为了追求效率而没有提供构造函数,要生成一个UUID值必须要使用生成器。我们
可以从uuid类派生一个可以自动产生UUID值的增强类,以简化UUID的使用。


4.10.6
uuid的名字生成器使用了SHA1摘要算法,该算法可以将任意长度的文本压缩成一个只有
20字节(160位)的独一无二的摘要。


sha1算法位于名字空间boost::uuids::detail,使用时需要包含头文件<boost/uuid/
sha1.hpp>
sha1类的用法很简单,使用成员函数process_byte()、process_block()和process_types()
以不同的方式把数据“喂”给sha1对象,当输入所有数据后用get_digest()获得计算
出的摘要值。


4.11 config
config库主要是提供给boost库开发者(而不是库用户)使用。它将程序的编译配置
分解为三个正交的部分:平台、编译器和标准库。


4.11.1 BOOST_STRINGIZE
宏BOOST_STRINGIZE可以将任意字面量转换为字符串。它在头文件
<boost/config/suffix.hpp>中
但它是宏,不支持运行时转换。如果要在运行时转换数字或者其他变量到字符串,请
使用之后的lexical_cast。


4.11.2 BOOST_STATIC_CONSTANT
C++98标准允许直接在类声明中为静态整形成员变量赋初始值。


4.12 utility
utility库不是一个有统一主题的Boost库,而是包含了若干个很小但有用的工具。


本章开头介绍的noncopyable、swap都被归类在utility库里,此外utility还包括其他
很多个实用类,如checked_delete、compressed_pair、base_from_memeber等。


4.12.1 BOOST_BINARY
BOOST_BINARY提供一组宏,用于实现简单的二进制常量表示。


它使用boost.preprocessor预处理元编程工具将一组或多组01数字在编译期展开成为一
个八进制数字。每个数字组之间可以用空格分隔,特别注意的是,数字组的长度一定不
能超过八个,由于预处理器宏展开的限制,嵌套层次太深会导致无法通过编译,报出
一大堆错误。


4.12.2 BOOST_CURRENT_FUNCTION
微软编译器VC在C89的__FILE__和__LINE__之外定义了一些扩展宏,其中的__FUNCTION__
宏可以表示函数名称,GCC、intel C等编译器也定义有类似的宏。而C99标准则定义了
__func__宏以实现同样的功能。但目前的C++98标准不能这样做。


在STL中,std::bitset不能直接使用字符串构造,必须使用bitset<5>(string("10110"))
这样的形式,导致生成一个string临时变量,效率较低。


它在头文件#include <boost/current_function.hpp>
只需要在代码中使用BOOST_CURRENT_FUNCTION宏,就可获得包含该宏的外围函数名称,
它表现为一个包含完整函数声明的编译期字符串。


应当总用boost::noncopyable的名字空间域限定形式来使用它,而不是用using语句,
避免在头文件打开boost名字空间。


在使用optional存储的元素之前,必须测试它的有效性。


assign库对标准容器、容器适配器和Boost容器都提供了很全面的支持,这使得它具有
很高的使用价值。


如果想要自己的类安全高效,那么应该提供一个好的swap函数,它是很多实用功能的
基础。如果你自己的类实现了高效的交换方法,那么boost::swap就会自动调用它。


Boost库提供了两种实现singleton的方式:pool.singleton和serialization.singleton。
两者都要求模板类型参数T具有缺省构造函数,而且构造的析构时不能抛出异常。


tribool实现了三态布尔逻辑。


operator库中的equality_comparable、less_than_comparable和totally_ordered是最
常用的操作符重载类,它们提供比较运算,被用于支持标准容器。


uuid组件实现了对UUID的表示和处理,提供了基于名字和随机数的生成算法生成全球
唯一的标识符,可以用在很多地方来唯一地标识对象。uuid库里还附带了一个SHA1算法
的实现,能够为任意长度的数据生成SHA1摘要。


最后是config和utility库,它们提供了几个很有用的宏:BOOST_STRINGIZE实现编译期
字符串转换;BOOST_STATIC_CONSTANT可以定义类的静态整型成员常量;BOOST_BINARY
便利了二进制数字的书写方法;BOOST_CURRENT_FUNCTION能够输出函数名称字符串。
还有头文件<boost/config/warning_disable.hpp>可以禁止编译器的C4996警告。




第5章
字符串与文本处理
字符串与文本处理一直是C++的弱项。虽然C++98标准提供了一个标准字符串类std::string
暂解燃眉之急,但C++仍然缺乏很多文本处理的高级特性,如正则表达式、分词等等,
使得不少C++程序员不得不转向其他语言(如perl、python)。


Boost中五个字符串与文件处理领域的程序库。首先是两个与C标准库函数功能类似的
lexical_cast和format,它们关注于字符串的表示,可以将数值转化为字符串,对输
出做精确的格式化。string_algo库提供了大量常用的字符串处理函数。剩下的可以
使用tokenizer和xpressive,前者是一个分词器,而后者则是一个灵活且功能强大的
正则表达式分析器,同时也是一个语法分析器。


使用Boost,C++中文本处理的一切问题将迎刃而解。


5.1 lexical_cast
lexical_cast库进行“字面量”的转换,类似C中的atoi函数,可以进行字符串、整数/
浮点数之间的字面转换。


lexical_cast位于名字空间boost,为了使用lexical_cast组件,需要包含头文件
<boost/lexical_cast.hpp>


Unicode和区域字符编码转换逐渐成为了软件开发中无法回避的问题。C++98标准定义了
wchar_t和locale等概念,C++0X还专门引入了<codecvt>。另外Boost的很多字符串的
处理都需要标准库的<locale>。


5.1.1用法
C语言中的atoi()、atof()系列函数,它们可以把字符串转换成数值,但不存在如itoa()
这样的返回转换(C语言标准未提供,但有的编译器厂商会提供非标准的如_itoa())。


使用lexical_cast可以很容易地在数值与字符串之间转换,只需要在模板参数中指定
转换的类型即可,


lexical_cast不能转换如"133L"、"0x33"这样的C++语法许可的数字字面量字符串。而且
lexical_cast不支持高级的格式控制,不能把数字转换成指定格式的字符串,如果需要
更高级的格式控制,可使用std::stringstream或者boost::format。


lexical_cast内部使用了标准库的流操作,因此,对于它的转换对象,有如下要求,
下义了operator<<, operator>>和是可缺省构造的可拷贝构造的。
C++中的内建类型都满足这些。


5.2 format
boost.format实现在类似于printf()的格式化对象,可以把参数格式化到一个字符串,
而且是完全类型安全的。


format组件位于名字空间boost,在头文件<boost/format.hpp>中。


boost.range基于STL迭代器提出了“范围”的概念,是一个容器的半开空间。


5.3 string_algo
string_algo库主要的工作对象还是字符串string和wstring,它也可以工作在标准容器
vector、deque、list和非标准容器slist、rope上,


前缀i:
后缀_copy
后缀_if


string_algo库提供的算法共分五大类,如下:
大小写转换
判断式与分类
修剪
查找与替换
分割与合并


5.3.3 大小写转换
包括两组算法:to_upper()和to_lower()。
还有前缀i和后缀_copy的版本。


5.3.4 判断式(算法)
starts_with
ends_with
contains
equals
lexicographical_compare
all


除了all,这些算法都有另一个i前缀的版本。由于它们不变动字符串,因此没有_copy
版本。
它们同时还有一种接受比较谓词函数对象的三参数版本,而没有使用_if后缀。


5.3.5 判断式(函数对象)
string_algo增强了标准库中的equal_to<>和less<>函数对象,允许对不同类型的参数
进行比较,并提供大小写无关的形式。这些函数对象有
is_equal
is_less
is_not_greater


5.3.6 分类
string_algo库的分类函数如下:
is_space
is_alnum
is_alpha
is_cntrl
is_digit
is_lower
is_graph
is_print
is_punct
is_upper
is_xdigit
is_any_of
if_from_range: 字符是否位于指定区间内,即from<=ch<=to
这些函数并不真正地检测字符,而是返回一个类型为detail::isclassifiedF的函数对象
这个函数对象的operator()才是真正的分类函数(因此,它些函数都属于工厂函数)。
函数对象is_classifiedF重载了逻辑运算符||,&&,和!,可以使用逻辑运算符把它们组
合成逻辑表达式,以实现更复杂的条件判断。当然,我们也可以自定义判断式。


5.3.7 修剪
string_algo提供3个修剪算法:trim_left, trim_right和trim
它有_if和_copy两种后缀,因此每个算法都有四个版本。


5.3.8 查找
string_algo使用了boost.range库的iterator_range返回查找到的整个区间。
string_algo提供的查找算法包括:
find_first
find_last
find_nth
find_head
find_tail
这些算法都不变动字符串,因此没有_copy后缀版本。但其中前三个算法有i前缀版本。
iterator_range可以像标准容器一样判断是否为空,也可以隐式转换为bool值。


string_algo库还有另外三个查找算法:find_token,find_regex和通用的find算法。


5.3.9 替换与删除
replace/erase_first
replace/erase_last
replace/erase_nth
replace/erase_all
replace/erase_head
replace/erase_tail
前八个算法每个都有前缀i、后缀_copy和组合,有四个版本,后四个则只有后缀_copy
的两个版本。


5.3.10 分割
string_algo提供两个字符串分割算法find_all和split
find_all算法类似于普通的查找算法,它搜索所有匹配的字符串,加入到容器中,有一
个忽略大小写的前缀i版本。


split算法使用判断式Pred来确定分割的依据,如果字符ch满足判断式Pred(Pred(ch)
==true),那么它就是一个分割符,将字符串从这里分割。


参数eCompress可以取值为token_compress_on或token_compress_off,如果值为前者,
那么当分隔符连续出现时将被视为一个,如果为token_compress_off则两个连续的分隔
符标记了一个空字符串。参数eCompress默认取值为token_compress_off。


5.3.11 合并
join还有一个后缀_if的版本,它接受一个判断式,只有满足判断式的字符串才能参与
合并。


5.3.12 查找(分割)迭代器
string_algo库还提供两个查找迭代器find_iterator和split_iterator,它们可以在字
符串中像迭代器那样遍历匹配,进行查找或者分割,无需使用容器来容纳。


使用查找迭代器首先要声明迭代器对象find_iterator或split_iterator,它们的模板
类型参数是一个迭代器类型a,例如string::iterator或char*


为了获得迭代器的起始位置,我们需要调用first_finder()函数,它用于判断匹配的对
象,然后再用make_find_iterator或make_split_iterator()来真正创建迭代器。同
族的查找函数还有last_finder,nth_finder,token_finder等。
例如:
typedef split_iterator<string::iterator> string_split_iterator;
string_split_iterator p, endp;
for (p = make_split_iterator(str, first_finder("||", is_iequal()));
p != endp; ++p)
{
cout<<"["<<*p<<"]";
}


5.4 tokenizer
tokenizer库一个专门用于分词tiken的字符串处理库,可以使用简单易用的方法把一个
字符串分解成若干个单词。
<boost/tokenizer.hpp>
using namespace boost;


tokenizer接受三个模板类型参数,分别是:
TokenizerFunc :tokenizer库专门的分词函数对象,默认是使用空格和标点分词
Iterator: 字符序列的迭代器类型
Type: 保存分词结果的类型


tokenizer的构造函数接受要进行分词的字符串,可以以迭代器的区间形式给出,也可
以是一个有begin()和end()成员函数的容器。
assign()函数可以重新指定要分词的字符串,用于再利用tokenizer。
tokenizer具有类似标准容器的接口,begin()函数使用tokenizer开始执行分词功能,
返回第一个分词的迭代器,end()函数表明迭代器已经到达分词序列的末尾,分词结束。


5.4.2 用法
tokenizer的用法很像string_algo的分割迭
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值