http://www.usidcbbs.com/read-htm-tid-1397.html
boost是一个准标准库,相当于STL的延续和扩充,它的设计理念和STL比较接近,都是利用泛型让复用达到最大化。不过对比STL,boost更加实用。STL集中在算法部分,而boost包含了不少工具类,可以完成比较具体的工作。
boost 主要包含一下几个大类:字符串及文本处理、容器、迭代子(Iterator)、算法、函数对象和高阶编程、泛型编程、模板元编程、预处理元编程、并发编程、数学相关、纠错和测试、数据结构、输入/输出、跨语言支持、内存相关、语法分析、杂项。 有一些库是跨类别包含的,就是既属于这个类别又属于那个类别。
在文本处理部分,conversion/lexcial_cast类用于“用C++”的方法实现数字类型和字符串之间的转换。 主要是替代C标准库中的 atoi、 itoa之类的函数。当然其中一个最大的好处就是支持泛型了。
format 库提供了对流的“printf-like”功能。printf里使用%d、%s等等的参数做替换的方法在很多情况下还是非常方便的,STL的 iostream则缺乏这样的功能。format为stream增加了这个功能,并且功能比原始的printf更强。
regex,这个不多说了,正则表达式库。如果需要做字符串分析的人就会理解正则表达式有多么有用了。
spirit,这个是做LL分析的框架,可以根据EBNF规则对文件进行分析。(不要告诉我不知道什么是EBNF)。做编译器的可能会用到。一般人不太用的到。
tokenizer 库。我以前经常在CSDN上看到有人问怎么把一个字符串按逗号分割成字符串数组。也许有些人很羡慕VB的split函数。现在,boost的 tokenizer也有相同的功能了,如果我没记错的话,这个tokenizer还支持正则表达式,是不是很爽?
array: 提供了常量大小的数组的一个包装,喜欢用数组但是苦恼数组定位、确定数组大小等功能的人这下开心了。
dynamic_bitset,动态分配大小的bitset,我们知道STL里有个bitset,为位运算提供了不少方便。可惜它的大小需要在编译期指定。现在好了,运行期动态分配大小的bitset来了。
graph。提供了图的容器和相关算法。我还没有在程序中用到过图,需要用的人可以看看。
multi_array提供了对多维数组的封装,应该还是比较有用的。
并发编程里只有一个库,thread,提供了一个可移植的线程库,不过在Windows平台上我感觉用处不大。因为它是基于Posix线程的,在Windows里对Posix的支持不是很好。
接下来的 数学和数值 类里,包含了很多数值处理方面的类库,数学类我也不太熟,不过这里有几个类还是很有用的,比如rational分数类,random随机数类,等等。
static_assert,提供了编译器的assert功能。
test库,一个单元测试框架,非常不错。
concept_check提供了泛型编程时,对泛型量的一点检查,不是很完善,不过比没有好。
数据类型类any,一个安全的可以包含不同对象的类。把它作为容器的元素类型,那么这个容器就可以包含不同类型的元素。比用void *要安全。
compressed_pair,跟STL里的pair差不多。不过对空元素做了优化。
tuple,呵呵,也许是某些人梦寐以求的东西。可以让函数返回多个值。
跨语言支持:python,呵呵,好东东啊,可以将C++的类和函数映射给python使用。以下为几个CSDN上的关于boost.python的中文资料:http://dev.csdn.net/article/19/19828.shtm,http://dev.csdn.net/article/19/19829.shtm,http://dev.csdn.net/article/19/19830.shtm,http://dev.csdn.net/article/19/19831.shtm
pool:内存池,呵呵,不用害怕频繁分配释放内存导致内存碎片,也不用自己辛辛苦苦自己实现了。
smart_ptr:智能指针,这下不用担心内存泄漏的问题了吧。不过,C++里的智能指针都还不是十全十美的,用的时候小心点了,不要做太技巧性的操作了。
date_time,这个是平台、类库无关的实现,如果程序需要跨平台,可以考虑用这个。
timer,提供了一个计时器,虽然不是Windows里那种基于消息的计时器,不过据说可以用来测量语句执行时间。
uitlity里提供了一个noncopyable类,可以实现“无法复制”的类。很多情况下,我们需要避免一个类被复制,比如代表文件句柄的类,文件句柄如果被两个实例共享,操作上会有很多问题,而且语义上也说不过去。一般的避免实例复制的方法是把拷贝构造和operator=私有化,现在只要继承一下这个类就可以了,清晰了很多。
value_initialized:数值初始化,可以保证声明的对象都被明确的初始化,不过这个真的实用吗?似乎写这个比直接写初始化还累。呵呵,仁者见仁了。
这里面除了regex、python和test需要编译出库才能用,其他的大部分都可以直接源代码应用,比较方便。其实这些库使用都不难。最主要的原因是有些库的使用需要有相关的背景知识,比如元编程、STL、泛型编程等等。
下载
1
Boost库通过SourceForge文件发布系统来进行发布。可以 从SourceForge下载得到Boost发行版. 并把它解压缩到一个合适的地方。
你从网站上可以得到Boost发行版的所有的库和其他文档。他们被打包为ZIP, TAR.GZ, and TAR.BZ2 文件格式。 (在这里)既可以得到老的版本。
也可以从CVS 库同步到当前Boost最新的快照版本。
.zip file
.zip 格式被自由和商业的压缩/存档工具广泛的支持。如果你还没有一个.zip文件解压器。那么可以从 Info-ZIP 网站下载一个,这个网站提供了不少操作系统上面的版本。文 本文件的行结束符如果打包为.zip 作为被每一个库开发者支持的(东西)(Text file line endings in the .zip file are as supplied by each library developer),在windows下面工作很好,在linux/unix下面就没那么漂亮了。.tar.gz和.tar.bz2支持对于 linux/unix更友好的文件换行符
.tar.gz 和 .tar.bz2 文件
.tar.gz 格式在Unix/Linux下面被广泛的支持。一些Windows的压缩/归档工具也可以顺利读取。因为gzip文档把所有文件看成一个文档压缩,所以要比分别压缩文件的zip压缩出来的文档要小一些。
.tar.bz2 格式在Unix/Linux下面被广泛的支持。并且其还被整合到一些tar工具中,这个格式采用了和.tar.gz不同的压缩方法。所以其可以创建更加小的文件。
为了在Unix/Linux环境下更容易的使用,Text文件换行符,在.tar.gz和.tar.ba2文件中已经被转换成一个新行。
Boost CVS 库
所有的Boost文档,包括实体发行树(代码),包括网站HTML文档都被CVS服务器管理。(Boost网站)提供了命令行,GUI,浏览器访问方式。
通过命令行或者CVS客户端访问CVS
(Boost)库可以从对那些安装了CVS客户端的,可以去访问公共Boost CVS 仓库。Unix/Linux环境的用户可以使用命令行来访问,Windows,Mac或者其他环境的用户则可以通过GUI来访问。
请参考改良过的SourceForgeCVS使用指南第F节。它包含了各种各样的客户端的访问链接。
常规的访问方法如下:
cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/boost login
[Hit when it asks for a password]
cvs -z3 -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/boost checkout boost
cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/boost logout
请阅读你的CVS手册来获得更多的信息。
这种访问是只读的;如果你是一个库的作者并且打算向CVS提交代码,请联系 相关人员。
通过网页浏览器访问Boost CVS
你可以近代的浏览器来访问 Boost CVS 文件 你可以通过颜色的不同看到一个文件被更新了多少次 注意:这个接口只是用来显示个别文件的修改历史 从CVS里的BoostBook产生 文档一些Boost文档产生自CVS仓库里面的BoostBook XML 代码,这些代码并没有被转换成可以阅读的HTML参考文档。可以访问 Nightly Generated 文档来查看通过"nightly build"生成的HTML文档。
准备
Boost推荐使用Boost.Build(Boost编译生成系统)来生成安装Boost库。 本文档剩下的部分解释它的用法。但是用不用这个系统就看你的需求了。注意,一些虽然库同样也包含了非Boost.Build的make文件或者工程文件。 但是Boost.Build却包含了构建Boost所需要的全部文件。
2
这个生成系统使用Boost.Jam,一个 Perforce Jam 扩展版本--这是make 一种轻量级的替代品。 推荐你去SourceForge 下载一个Boost.jam的可执行文件 如果你所使用的环境没有预先编译好的可执行的Boost.Jam或者如果你在使用一个还没有发布的Boost源码。那么你就需要 从代码手动生成bjam , 而bjam的代码也包含在Boost的源码树里面。编译好Boost.Jam以后,把bjam的执行文件放在你的Boost根目录下面就可以了。
配置编译工具
在使用Boost.Build之前,你需要指定编译工具,有两种方法可以做到使得Build.System正常的工作。
- 用户可以用常规的方法为它他算使用的每一套编译工具集设置一套变量。 例如,设置Microsoft VC++, 就可以在PATH变量中添加...VC98/Bin 或者 .../VC7/Bin的路径, 还需要编译之前调用一次VCVAR32.BAT或者VSVAR32.BAT(这样它就可以用VC来编译Boost了)。 而使用CodeWarrior , 就应该调用cwenv.bat,而且其他的Metrowerks工具/命令行工具也应该在path变量中。 大多数的Unix系统都会预先配置好这些变量而不需要用户来干预。
- 某一个用户不打算让他的变量被弄乱设置或者他也不是很了解他的编译工具的配置。作为一种替代,他(她)可以设置一个变量指向她的编译工具的安装目录, 这个变量可以事先在命令行环境变量里面声明,或者在调用
bjam
时声明, 知道定义好这个变量, 那些被用到的变量会被自动的找到并且设置好。这个变量就是-s,例如:
bjam "-sGCC_ROOT_DIRECTORY=/usr/local/gcc-3.3.2"
一些变量,例如工具集变量TOOLS ,可以接收多个用空格分开的值。 其他的,比如上述的path,可以包含空格。对于一些环境,你还可以引用其他适当的设置方法(译者注:这可能是说可以使用shell变量一类的东西)。
被支持的编译工具 见链接
http://www.usidcbbs.com/read-htm-tid-1397.html
生成物
生成物包括:静态库,动态库,而不同的环境会让库的名称也不一样。一个简单的Boost库默认将会产生八个不同的库。 例如Boost.Datetime库在Unix 类系统下面将会被处理成:
- libboost_date_time-gcc-d-1_31.so
- libboost_date_time-gcc-mt-d-1_31.so
- libboost_date_time-gcc-1_31.so
- libboost_date_time-gcc-mt-1_31.so
- libboost_date_time-gcc-d-1_31.a
- libboost_date_time-gcc-mt-d-1_31.a
- libboost_date_time-gcc-1_31.a
- libboost_date_time-gcc-mt-1_31.a
· 库前缀
lib
· 库名称
boost_date_time
· 编译工具
-
gcc
· 线程模式
-
mt
· 运行模式
-
d
· Boost 版本
-
1_31
· 库类型
.a
库前缀
一些系统要求库应该具有“lib”前缀,例如Unix,和其他的例如在Winodws运行的GCC,因此在所有的Unix类型的系统和Windows上使用的Boost静态库上面都会被加上这个前缀。 而Unix共享库,和windows静态库则被分别命名为
- lib*.so
- lib*.a
而Winodws的共享库则没有prefix前缀来区别“import libraries”和"static libraries"。因此在Windows下面, 共享库被命名为:
*.dll
共享库版本
*.lib
dll(共享库)的"Import library"
lib*.lib
"Static library"版本
库名称
Boost"会用boost_" 前缀来命名库用以区别您的系统上其他的库。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=930489
2006-12-08 12:34
(zz)
首先从Boost的老巢http://www.boost.org/下载。下载完成以后是一个安装文件(其实就是进行自动解压缩的)。选择你的目录解压缩Boost库。然后我们开始对其进行编译我所下载的版本是1.33.1。
1.cmd
2.进入安装VC的目录例如c:/Program Files/Microsoft Visual Studio/VC98/Bin目录
3.运行VCVARS32.BAT(这个操作的作用是对此控制台注册VC的环境变量,这样我们就可以在任意目录使用cl进行编译程序)。
4.进入Boost的目录例如c:/boost_1_33_1/tools/build/jam_src
5.修改build.bat脚本的参数(如果必要的话,你的安装目录不是c:盘)
将原参数
if EXIST "C:/Program Files/Microsoft Visual Studio/VC98/bin/VCVARS32.BAT" (
set BOOST_JAM_TOOLSET=msvc
set BOOST_JAM_TOOLSET_ROOT=C:/Program Files/Microsoft Visual Studio/VC98/
goto :eof)修改为(就是你的VC6.0安装的目录,我这里举个例子)
if EXIST "D:/Program Files/Microsoft Visual Studio/VC98/bin/VCVARS32.BAT" (
set BOOST_JAM_TOOLSET=msvc
set BOOST_JAM_TOOLSET_ROOT=D:/Program Files/Microsoft Visual Studio/VC98/
goto :eof)6.在刚才打开的控制台下运行
D:/d/boost_1_33_1/tools/build/jam_src/build.bat
最终会在目录下生成一个名为bin.ntx86的文件夹里面包含一个bjam.exe可执行文件,将其拷贝到D:/boost_1_33_1目录下
7.安装python可以在[url]www.python.org[/url]获得
8.设置环境变量
SET MSVC_ROOT="c:/Program Files/Microsoft Visual Studio/VC98"
SET VISUALC="c:/Program Files/Microsoft Visual Studio/VC98"
SET JAM_TOOLSET=VISUALC
SET PYTHON_ROOT=D:/Program Files/Python23
SET PYTHON_VERSION=2.39开始编译
bjam -sTOOLS=msvc install(将会安装在默认目录C:/Boost)
这样编译就完成了
10.boost类库在VC6.0的IDE环境中的配置
Tools” -> “Options” -> “Diretories” -> “Show Diretories for”中的下拉框选择:
1) 在Include files 中添加C:/BOOST/INCLUDE/BOOST-1_33_1/BOOST目录。
2) 在Library files 中添加C:/BOOST/LIB目录。11.测试
如果你不能确定,你的编译是否成功。就需要进行一下测试,拿正则表达式类为例。
测试如下程序
#include "stdafx.h"
#include
#include
#include <boost/regex.hpp>
#include
#includeusing namespace std;
using namespace boost;regex expression("^select ([a-zA-Z]*) from ([a-zA-Z]*)");
int main(int argc, char* argv[])
{
std::string in;
cmatch what;
cout << "enter test string" << endl;
getline(cin,in);
if(regex_match(in.c_str(), what, expression))
{
for(int i=0;i
cout<<"str :"<.str()<
}
else
{
cout<<"Error Input"<
}
return 0;
}输入:select name from database;
输出:str:select name from table
str:name
str:table
这样就完成了单个库编译方法:(以regex为例)
编译boost.regex的另一种办法
1.运行vcvars32.bat,可以帮助设置好vc命令行环境
2.cd boost_1_33_1/libs/regex/build
3.nmake -vc6.mak
这种方式可以只编译regex
这时会在当前目录下生成vc6目录,里面有已编译好的lib和dll文件, 这样以后的步骤中可以设置regex库时指向这里, 包含文件需要设置到boost_1_33_1目录
另修正补充:
用bjam编译regex时,选项-sTOOLS=vc7中vc7改成vc71,否则下面最终生成工程时需要改个文件名(
libboost_regex-vc7-mt-sgd-1_33_1.lib ->
libboost_regex-vc71-mt-sgd-1_33_1.lib)boost::regex的用法
1 常用类
1.2 basic_regex 类
定义:
template >
class basic_regex;
basic_regex生成和处理一个正则表达式,这个类的用法很简单,俱体用法参考文献[2]。
1.2 sub_match类
这个类处理一个子表达式的匹配结果,定义如下:
template
class sub_match : public std::pair
{
public:
typedef typename iterator_traits ::value_type value_type;
bool matched;
... ...
};
这个类表示和处理一个子串匹配结果。
match成员变量指示匹配是否成功。
注意到了这个类是从std::pair派生的,所以它有first和second这两个数据成员,如果匹配成功,first保存匹配字符子串的开始位置,second保存匹配字符串的结束位置。
sub_match的其它信息请参考文献[2]。
1.3 match_results
这个类处理sub_match的std::vector集合,保存多个匹配子串的结果。
template
class match_results
{
private:
typedef std::vector > vector_type;
typedef sub_match value_type;
private:
vector_type m_subs; // subexpressions
};
从上面的定义可以看出,这个类主要处理std::vector > m_subs。这个类在下面要介绍的几个接口函数中要用到。
掌握了match_results大体结构就很容易理解它的作用。其它操作信息可以参考文献[2]。
2 常用算法
2.1 算法1——regex_match
regex_match(..)是一个全局模板函数,是boost::regex最基本用法。它作用是根据正则表达式确定整个字符序列是否符合一定的格式要求,模式由正则表达式确定。它最常用的地方是输入合法性的检验。
原型1:
template
bool regex_match(
BidirectionalIterator first,
BidirectionalIterator last,
match_results & m,
const basic_regex & e,
match_flag_type flags = match_default);
first, last确定要匹配的目标序列,m是存放匹配的结果(见3.2.3),e就是输入的正则表达式,flags是匹配标识。返回值如果是true,则说明匹配成功;flase,说明不匹配。
上面这个算法函数包含一个匹配结果参数;一般情况下,用这个算法的时候,我们只要得到是否匹配结果就行了。boost::regex同样提供一个重载的函数:
原型2:
template
bool regex_match(
BidirectionalIterator first,
BidirectionalIterator last,
const basic_regex & e,
match_flag_type flags = match_default);
与上一个重载函数相比,就少了一个match_results & m, 用起来更方便。regex_match还有几个重函数,只是参数有变化,总得用法和上面介绍的两个函数是一样的。
最后要指出的是regex_match是匹配整个字符序列是否符输入的正则表达式的要求;如果我们找出字符序列哪些子序列符合输入的正则表达式的要求,那就要用regex_search算法。regex_seach(..)就是下节要介绍的一个模板算法。
2.2 算法2——regex_search
regex_search是用boost::regex最常用到的模板算法。它的作用是字符序列中哪些子序列是匹配输入的正则表达式的。它的函数原型与regex_match差不多,只是在用法复杂了一些。
原型1:
template class Allocator, class charT, class traits>
bool regex_search(
BidirectionalIterator first,
BidirectionalIterator last,
match_results & m,
const basic_regex & e,
match_flag_type flags = match_default);
参数first和last确定要匹配的目标序列,m存放匹配结果,e就是输入的正则表达式, flags是匹配标识。
如果本次匹配成功,返回true;否返回的是false。
regex_search还有几个重载函数,用法与上面的相同,或者更简单,所以在这里不作介绍。它们的原型请参考文献[2]。
上面介绍的两种模板算法基本上可以满足正则表达式应用的要求。boost::regex还包含其它一些算法,在某些应用中可以发挥作用,俱体用法请参考文献[2]。
3 用法举例
上面介绍正则表达式语法、basic_regex以及regex_match、regex_search两个模板算法。现在就根据这些知识实现两个有意义的算法。
3.1 regex_match
给出一个卡号验证程序,这个程序在文献[4]中可以找到。
#include
#include
#include
// 验证卡号是否与正则表达式匹配
bool validate_card_format(const std::string& s)
{
static const boost::regex e("( //d{4}[- ]){3}//d{4}");
return boost::regex_match(s, e);
}
int main()
{
std::string s[4] = { "0000111122223333", "0000 1111 2222 3333",
"0000-1111-2222-3333", "000-1111-2222-3333", };
int i;
for (i = 0; i < 4; ++i)
{
std::cout << "validate_card_format(/"" << s << "/") returned "
<< validate_card_format(s) << std::endl;
}
return 0;
}
3.2 regex_search
下面程序参考了文献[4],作用是输入有2个子表达式的正则表达式,获得匹配结果以及匹配结果在整个序列中的开始索引。
const char* re = "(人)|(a)";
boost::regex expression(re, boost::regex_constants::normal);
typedef std::pair pair_type;
typedef std::vector vec_type;
void search(const std::string& file, vec_type& vv)
{
std::string::const_iterator start, end;
start = file.begin();
end = file.end();
boost::match_results what;
boost::match_flag_type flags = boost::match_default | boost::match_any;
vec_type v1; // contain full expression
vec_type v2; // contain (人) sub expression
vec_type v3; // contain (a) sub expression
while (boost::regex_search(start, end, what, expression, flags))
{
pair_type r;
std::string::const_iterator temp = end;
if (what[0].matched)
{
r.first = std::string(what[0].first, what[0].second);
r.second = what[0].first - file.begin();
v1.push_back(r);
temp = what[0].second;
}
if (what[1].matched) //
{
r.first = string(what[1].first, what[1].second);
r.second = what[1].first - file.begin();
v2.push_back(r);
if (temp - what[1].second > 0)
temp = what[1].second;
}
if (what[2].matched)
{
r.first = string(what[2].first, what[2].second);
r.second = what[2].first - file.begin();
v3.push_back(r);
if (temp - what[2].second > 0)
temp = what[2].second;
}
start = temp;
flags = boost::match_default | boost::match_any;
}
vv.push_back(v1);
vv.push_back(v2);
vv.push_back(v3);
}
上面的代码是与易懂为原则,所以代码写得比较松散。
4 对各种编码的支持
在实际应用开发中,有几种常用的编码方案,包括针对中文编码的GB2312、GB18030,UTF7、UTF8等,还有统一编码方案Unicode。
regex.hpp中有下面两行代码:
typedef basic_regex regex;
typedef basic_regex wregex;
中文编码的正则表达式用regex实现就行了;而对于Unicode编码用wregex实现。在这里要说明一下,wchar_t*并不是Unicode字符串。俱体什么是Unicode可以参考文献[5]。
5 参考文献
[1] www.boost.org boost库的官方网站,这套库包含许多有用的组件;
[2] boost_1_32_0 boost::regex附带文档;
[3] boost_1_32_0 boost::regex实现源码;
[4] boost_1_32_0 boost::regex附带examples;
[5] www.unicode.org 在这里可以找什么是unicode的主题。
boost::regex的用法
- boost::regex的用法
bool validate_card_format(const std::string s)
{
static const boost::regex e("(//d{4}[- ]){3}//d{4}");
return regex_match(s, e);
}
- boost::regex的默认正则表达式语法是perl语法
例如,下面两种方法效果相同
// e1 is a case sensitive Perl regular expression:
// since Perl is the default option there's no need to explicitly specify the syntax used here:
boost::regex e1(my_expression);
// e2 a case insensitive Perl regular expression:
boost::regex e2(my_expression, boost::regex::perl|boost::regex::icase);
- perl正则表达式语法
. 任意字符;使用match_no_dot_null标志时不匹配NULL字符; 使用match_not_dot_newline时不匹配换行字符
^ 匹配行的开始
$ 匹配行的结束
* 重复零次或则更多,例如a*b可匹配b,ab,aab,aaaaaaab
+ 重复一次以上,例如a+b可匹配ab,aab,aaaaaaaab。但不能匹配b了
? 零次或则一次,例如ca?b匹配cb,cab但不匹被caab
a{n} 匹配字符'a'重复n次
a{n,},字符a重复n次以上(含n次)
a{n,m} a重复n到m次(含)
*? 匹配前一个原子零次以上
+? 匹配前一个原子一次以上
?? 匹配前一个原子零次以上
{n,}? 匹配前一个原子n次以上(含)
{n,m}? 匹配前一个原子n到m次(含)
| 或操作,例如ab(d|ef)匹配abd或则abef
[] 字符集操作,例如[abc]将匹配任何单个字符'a','b','c'
[a-d],表示a、b、c、d
^否操作,例如[^a-c]表示a至c之外的所有字符
- boost::regex对unicode编码的支持
- 搜索时如何忽略大小写
如果要在搜索时忽略大小写(即大小写不敏感),则要用到表达式选项boost::regex::icase,例如: boost::regex e2(my_expression, boost::regex::perl|boost::regex::icase);
boost-regex详细教程
头文件:"boost/regex.hpp"
正则表达式被封装为一个类型basic_regex的对象。我们将在下一节更深入地讨论正则表达式如何被编译和分析,这里我们首先粗略地看看basic_regex,以及这个库中三个最重要的算法。
namespace boost {
template
class traits=regex_traits
>
class basic_regex {
public:
explicit basic_regex(
const charT* p,
flag_type f=regex_constants::normal);
bool empty() const;
unsigned mark_count() const;
flag_type flags() const;
};
typedef basic_regex
regex;
typedef basic_regex
wregex;
}
成员函数
explicit basic_regex (
const charT* p,
flag_type f=regex_constants::normal);这个构造函数接受一个包含正则表达式的字符序列,还有一个参数用于指定使用正则表达式时的选项,例如是否忽略大小写。如果p中的正则表达式无效,则抛出一个bad_expression或regex_error的异常。注意这两个异常其实是同一个东西;在写这本书之时,尚未改变当前使用的名字bad_expression,但下一个版本的Boost.Regex将会使用regex_error.
bool empty() const;这个成员函数是一个谓词,当basic_regex实例没有包含一个有效的正则表达式时返回true,即它被赋予一个空的字符序列时。
unsigned mark_count() const;mark_count返回regex中带标记子表达式的数量。带标记子表达式是指正则表达式中用圆括号括起来的部分。匹配这个子表达式的文本可以通过调用某个正则表达式算法而获得。
flag_type flags() const;返回一个位掩码,其中包含这个basic_regex所设置的选项标志。例如标志icase, 表示正则表达式忽略大小写,标志JavaScript, 表示regex使用JavaScript的语法。
typedef basic_regex
regex;
typedef basic_regex
wregex;不要使用类型basic_regex来定义变量,你应该使用这两个typedef中的一个。这两个类型,regex和wregex, 是两种字符类型的缩写,就如string和wstring是basic_string
和basic_string
的缩写一样。这种相似性是不一样的,某种程度上,regex是一个特定类型的字符串的容器。
普通函数
template
bool regex_match(
const charT* str,
match_results
& m,
const basic_regex
& e,
match_flag_type flags = match_default);
regex_match判断一个正则表达式(参数e)是否匹配整个字符序列str. 它主要用于验证文本。注意,这个正则表达式必须匹配被分析串的全部,否则函数返回false. 如果整个序列被成功匹配,regex_match返回True.
template
bool regex_search(
const charT* str,
match_results
& m,
const basic_regex
& e,
match_flag_type flags = match_default);
regex_search类似于regex_match, 但它不要求整个字符序列完全匹配。你可以用regex_search来查找输入中的一个子序列,该子序列匹配正则表达式e.
template
basic_string
regex_replace(
const basic_string
& s,
const basic_regex
& e,
const basic_string
& fmt,
match_flag_type flags = match_default);
regex_replace
在整个字符序列中查找正则表达式e的所有匹配。这个算法每次成功匹配后,就根据参数fmt对匹配字符串进行格式化。缺省情况下,不匹配的文本不会被修改,即文本会被输出但没有改变。
这三个算法都有几个不同的重载形式:一个接受const charT*(charT为字符类型), 另一个接受const basic_string
&, 还有一个重载接受两个双向迭代器作为输入参数。
用法
要使用Boost.Regex, 你需要包含头文件"boost/regex.hpp". Regex是本书中两个需要独立编译的库之一(另一个是Boost.Signals)。你会很高兴获知如果你已经构建了Boost— —那只需在命令提示符下打一行命令——就可以自动链接了(对于Windows下的编译器),所以你不需要为指出那些库文件要用而费心。
你要做的第一件事就是声明一个类型basic_regex的变量。这是该库的核心类之一,也是存放正则表达式的地方。创建这样一个变量很简单;只要将一个含有你要用的正则表达式的字符串传递给构造函数就行了。
boost::regex reg("(A.*)");这个正则表达式具有三个有趣的特性。
第一个是,用圆括号把一个子表达式括起来,这样可以稍后在同一个正则表达式中引用它,或者取出匹配它的文本。我们稍后会详细讨论它,所以如果你还不知道它有什么用也不必担心。第二个是,通配符(wildcard)字符,点。这个通配符在正则表达式中有非常特殊的意义;这可以匹配任意字符。最后一个是,这个表达式用到了一个重复符,*, 称为Kleene star, 表示它前面的表达式可以被匹配零次或多次。这个正则表达式已可以用于某个算法了,如下:
bool b=boost::regex_match(
"This expression could match from A and beyond.",
reg);如你所见,你把正则表达式和要分析的字符串传递给算法regex_match. 如果的确存在与正则表达式的匹配,则该函数调用返回结果true;否则,返回false. 在这个例子中,结果是false, 因为regex_match仅当整个输入数据被正则表达式成功匹配时才返回true。你知道为什么是这样吗?再看一下那个正则表达式。第一个字符是大写的A, 很明显能够匹配这个表达式的第一个字符在哪。所以,输入的一部分"A and beyond."可以匹配这个表达式,但这不是整个输入。让我们试一下另一个输入字符串。
bool b=boost::regex_match(
"As this string starts with A, does it match ",
reg);这一次,regex_match返回true. 当正则表达式引擎匹配了A, 它接着看后续有什么。
在我们的regex变量中,A后跟一个通配符和一个Kleene star, 这意味着任意字符可以被匹配任意次。因而,分析过程开始扔掉输入字符串的剩余部分,即匹配了输入的所有部分。
接下来,我们看看如何使用regexes 和regex_match来进行数据验证。
验证输入
正则表达式常用于对输入数据的格式进行验证。应用软件通常要求输入符合某种结构。考虑一个应用软件,它要求输入一定要符合如下格式,"3个数字, 一个单词, 任意字符, 2个数字或字符串"N/A," 一个空格, 然后重复第一个单词." 手工编写代码来验证这个输入既沉闷又容易出错,而且这些格式还很可能会改变;在你弄明白之前,可能就需要支持其它的格式,你精心编写的分析器可能就需要修 改并重新调试。让我们写出一个可以验证这个输入的正则表达式。首先,我们需要一个匹配3个数字的表达式。对于数字,我们应该使用一个特别的缩写,/d。要表示它被重复3次,需要一个称为bounds operator的特定重复,它用花括号括起来。把这两个合起来,就是我们的正则表达式的开始部分了。
boost::regex reg("//d{3}");注意,我们需要在转义字符(/)之前加一个转义字符,即在我们的字符串中,缩写/d变成了//d。这是因为编译器会把第一个/当成转义字符扔掉;我们需要对/进行转义,这样/才可以出现在我们的正则表达式中。
接下来,我们需要定义一个单词的方法,即定义一个字符序列,该序列结束于一个非字母字符。有不只一种方法可以实现它,我们将使用字符类别(也称为字符集)和范围这两个正则表达式的特性来做。
字符类别即一个用方括号括起来的表达式。例如,一个匹配字符a,b, 和c中任一个的字符类别表示为:[abc]. 如果用范围来表示同样的东西,我们要写:[a-c]. 要写一个包含所有字母的字符类型,我们可能会有点发疯,如果要把它写成:[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ], 但不用这样;我们可以用范围来表示:[a-zA-Z]. 要注意的是,象这样使用范围要依赖于当前所用的locale,如果正则表达式的basic_regex::collate标志被打开。使用以上工具以及重复符+, 它表示前面的表达式可以重复,但至少重复一次,我们现在可以表示一个单词了。
boost::regex reg("[a-zA-Z]+");以上正则表达式可以工作,但由于经常要表示一个单词,所以有一个更简单的方法:/w. 这个符号匹配所有单词,不仅是ASCII的单词,因此它不仅更短,而且也更适用于国际化的环境。接下来的字符是一个任意字符,我们已经知道要用点来表示。
boost::regex reg(".");再接下来是2个数字或字符串 "N/A."为了匹配它,我们需要用到一个称为
选择的特性。选择即是匹配两个或更多子表达式中的任意一个,每种选择之间用|分隔开。就象这样:
boost::regex reg("(//d{2}|N/A)");注意,这个表达式被圆括号括了起来,以确保整个表达式被看作为两个选择。在正则表达式中增加一个空格是很简单的;用缩写/s. 把以上每一样东西合并起来,就得到了以下表达式:
boost::regex reg("//d{3}[a-zA-Z]+.(//d{2}|N/A)//s");现在事情变得有点复杂了。我们需要某种方法,来验证接下来的输入数据中的单词是否匹配第一个单词(即那个我们用表达式[a-zA-Z]+所捕获的单词)。关键是要使用后向引用(back reference),即对前面的子表达式的引用。为了可以引用表达式[a-zA-Z]+, 我们必须先把它用圆括号括起来。这使得表达式([a-zA-Z]+)成为我们的正则表达式中的第一个子表达式,我们就可以用索引1来建立一个后向引用了。
这样,我们就得到了整个正则表达式,用于表示"3个数字, 一个单词, 任意字符, 2个数字或字符串"N/A," 一个空格, 然后重复第一个单词.":
boost::regex reg("//d{3}([a-zA-Z]+).(//d{2}|N/A)//s//1");干的好!下面是一个简单的程序,把这个表达式用于算法regex_match, 验证两个输入字符串。
#include
#include
#include
#include "boost/regex.hpp"
int main() {
// 3 digits, a word, any character, 2 digits or "N/A",
// a space, then the first word again
boost::regex reg("//d{3}([a-zA-Z]+).(//d{2}|N/A)//s//1");
std::string correct="123Hello N/A Hello";
std::string incorrect="123Hello 12 hello";
assert(boost::regex_match(correct,reg)==true);
assert(boost::regex_match(incorrect,reg)==false);
}第一个字符串,123Hello N/A Hello, 是正确的;123是3个数字,Hello是一个后跟任意字符(一个空格)的单词, 然后是N/A和另一个空格,最后重复单词Hello。第二个字符串是不正确的,因为单词Hello没有被严格重复。缺省情况下,正则表达式是大小写敏感的,因而反向引用不能匹配。
写出正则表达式的一个关键是成功地分解问题。看一下你刚才建立的最终的那个表达式,对于未经过训练的人来说它的确很难懂。但是,如果把这个表达式分解成小的部分,它就不太复杂了。
查找
现在我们来看一下另一个Boost.Regex算法,regex_search. 与regex_match不同的是,regex_search不要求整个输入数据完全匹配,则仅要求部分数据可以匹配。作为说明,考虑一个程序员的问题,他可能在他的程序中有一至两次忘记了调用delete。虽然他知道这个简单的测试可能没什么意义,他还是决定计算一下new和delete出现的次数,看看数字是否符合。这个正则表达式很简单;我们有两个选择,new 和 delete.
boost::regex reg("(new)|(delete)");有两个原因我们要把子表达式用括号括起来:一个是为了表明我们的选择是两个组。
另一个原因是我们想在调用regex_search时引用这些子表达式,这样我们就可以判断是哪一个选择被匹配了。我们使用regex_search的一个重载,它接受一个match_results类型的参数。当regex_search执行匹配时,它通过一个match_results类型的对象报告匹配的子表达式。类模板match_results使用一个输入序列所用的迭代器类型来参数化。
template
class Allocator=std::allocator
>
class match_results;
typedef match_results
cmatch;
typedef match_results
wcmatch;
typedef match_results
smatch;
typedef match_results
wsmatch;
我们将使用std::string, 所以要留意typedef smatch, 它是match_results
的缩写。如果regex_search返回true, 传递给该函数的match_results引用将包含匹配的子表达式结果。在match_results里,用已索引的sub_match来表示正则表达式中的每个子表达式。我们来看一下我们如何帮助这位困惑的程序员来计算对new和delete的调用。
boost::regex reg("(new)|(delete)");
boost::smatch m;
std::string s=
"Calls to new must be followed by delete. /
Calling simply new results in a leak!";
if (boost::regex_search(s,m,reg)) {
// Did new match
if (m[1].matched)
std::cout << "The expression (new) matched!/n";
if (m[2].matched)
std::cout << "The expression (delete) matched!/n";
}
以上程序在输入字符串中查找new或delete, 并报告哪一个先被找到。通过传递一个类型smatch的对象给regex_search, 我们可以得知算法如何执行成功的细节。我们的表达式中有两个子表达式,因此我们可以通过match_results的索引1得到子表达式new. 这样我们得到一个sub_match实例,它有一个Boolean成员,matched, 告诉我们这个子表达式是否参与了匹配。
因此,对于上例的输入,运行结果将输出"The expression (new) matched!/n". 现在,你还有一些工作要做。你需要继续把正则表达式应用于输入的剩余部分,为此,你要使用另外一个regex_search的重载,它接受两个迭代器,指示出要查找的字符序列。因为std::string是一个容器,它提供了迭代器。现在,在每一次匹配时,你必须把指示范围起始点的迭代器更新为上一次匹配的结束点。最后,增加两个变量来记录new和delete的次数。
以下是完整的程序:
#include
#include
#include "boost/regex.hpp"
int main() {
// "new" and "delete" 出现的次数是否一样?
boost::regex reg("(new)|(delete)");
boost::smatch m;
std::string s=
"Calls to new must be followed by delete. /
Calling simply new results in a leak!";
int new_counter=0;
int delete_counter=0;
std::string::const_iterator it=s.begin();
std::string::const_iterator end=s.end();
while (boost::regex_search(it,end,m,reg)) {
// 是 new 还是 delete
m[1].matched ++new_counter : ++delete_counter;
it=m[0].second;
}
if (new_counter!=delete_counter)
std::cout << "Leak detected!/n";
else
std::cout << "Seems ok.../n";
}
注意,这个程序总是把迭代器it设置为m[0].second。match_results[0]返回对匹配整个正则表达式的子匹配的引用,因此我们可以确认这个匹配的结束点就是下次运行regex_search的起始点。运行这个程序将输出"Leak detected!", 因为这里有两次new, 而只有一次delete. 当然,一个变量也可能在两个地方删除,还有可能调用new[]和delete[], 等等。
现在,你应该已经对子表达式如何分组使用有了较好的了解。现在是时候进入到最后一个Boost.Regex算法,该算法用于执行替换工作。
替换
Regex算法家族中的第三个算法是regex_replace. 顾名思义,它是用于执行文本替换的。它在整个输入数据中进行搜索,查找正则表达式的所有匹配。对于表达式的每一个匹配,该算法调用match_results::format并输入结果到一个传入函数的输出迭代器。
在本章的介绍部分,我给出了一个例子,将英式拼法的colour替换为美式拼法color. 不使用正则表达式来进行这个拼写更改会非常乏味,也很容易出错。问题是可能存在不同的大小写,而且会有很多单词被影响,如colourize. 要正确地解决这个问题,我们需要把正则表达式分为三个子表达式。
boost::regex reg("(Colo)(u)(r)",
boost::regex::icase|boost::regex::perl);
我们将要去掉的字母u独立开,为了在所有匹配中可以很容易地删掉它。另外,注意到这个正则表达式是大小写无关的,我们要把格式标志boost::regex::icase传给regex的构造函数。你还要传递你想要设置的其它标志。设置标志时一个常见的错误就是忽略了regex缺省打开的那些标志,如果你没有设置这些标志,它们不会打开,你必须设置所有你要打开的标志。
调用regex_replace时,我们要以参数方式提供一个格式化字符串。该格式化字符串决定如何进行替换。在这个格式化字符串中,你可以引用匹配的子表达式,这正是我们想要的。你想保留第一个和第三个匹配的子表达式,而去掉第二个(u)。表达式$N表示匹配的子表达式,N为子表达式索引。因此我们的格式化串应该是"$1$3", 表示替换文本为第一个和第三个子表达式。通过引用匹配的子表达式,我们可以保留匹配文本中的所有大小写,而如果我们用字符串来作替换文本则不能做到这一点。以下是解决这个问题的完整程序。
#include
#include
#include "boost/regex.hpp"
int main() {
boost::regex reg("(Colo)(u)(r)",
boost::regex::icase|boost::regex::perl);
std::string s="Colour, colours, color, colourize";
s=boost::regex_replace(s,reg,"$1$3");
std::cout << s;
}程序的输出是"Color, colors, color, colorize".regex_replace对于这样的文本替换非常有用。
用户常见的误解
我所见到的与Boost.Regex相关的最常见的问题与regex_match的语义有关。人们很容易忘记必须使regex_match的所有输入匹配给定的正则表达式。因此,用户常以为以下代码会返回true.
boost::regex reg("//d*");
bool b=boost::regex_match("17 is prime",reg);无疑这个调用永远不会得到成功的匹配。只有所有输入被regex_match匹配才可以返回true!几乎所有的用户都会问为什么regex_search不是这样而regex_match是。
boost::regex reg("//d*");
bool b=boost::regex_search("17 is prime",reg);这次肯定返回true. 值得注意的是,你可以用一些特定的缓冲操作符来让regex_search象regex_match那样运行。/A匹配缓冲的起始点,而/Z匹配缓冲的结束点,因此如果你把/A放在正则表达式的开始,把/Z放在最后,你就可以让regex_search象regex_match那样使用,即必须匹配所有输入。以下正则表达式要求所有输入被匹配掉,而不管你使用的是regex_match或是regex_search.
boost::regex reg("//A//d*//Z");请记住,这并不表示可以无需使用regex_match;相反,它可以清晰地表明我们刚才说到的语义,即必须匹配所有输入。
关于重复和贪婪
另一个容易混淆的地方是关于重复的贪婪。有些重复,如+和*,是贪婪的。即是说,它们会消耗掉尽可能多的输入。以下正则表达式并不罕见,它用于在一个贪婪的重复后捕获两个数字。
boost::regex reg("(.*)(//d{2})");这个正则表达式是对的,但它可能不能匹配你想要的子表达式!表达式.*会吞掉所有东西而后续的子表达式将不能匹配。以下是示范这个行为的一个例子:
int main() {
boost::regex reg("(.*)(//d{2})");
boost::cmatch m;
const char* text = "Note that I'm 31 years old, not 32.";
if(boost::regex_search(text,m, reg)) {
if (m[1].matched)
std::cout << "(.*) matched: " << m[1].str() << '/n';
if (m[2].matched)
std::cout << "Found the age: " << m[2] << '/n';
}
}在这个程序中,我们使用了match_results的另一个特化版本,即类型cmatch. 它就是match_results
的typedef, 之所以我们必须用它而不是用之前用过的smatch,是因为我们现在是用一个字符序列调用regex_search而不是用类型std::string来调用。你期望这个程序的运行结果是什么?通常,一个刚开始使用正则表达式的用户会首先想到m[1].matched和m[2].matched都为true, 且第二个子表达式的结果会是 "31". 接着在认识到贪婪的重复所带来的效果后,即重复会尽可能消耗输入,用户会想到只有第一个子表达式是true,即.*成功地吞掉了所有的输入。最后,新用户得到了下结论,两个子表达式都被匹配,但第二个表达式匹配的是最后一个可能的序列。即第一个子表达式匹配的是 "Note that I'm 31 years old, not" 而第二个匹配 "32".
那么,如果你想使用重复并匹配另一个子表达式的第一次出现,该怎么办?要使用非贪婪的重复。在重复符后加一个,重复就变为非贪婪的了。这意味着该表达式会尝试发现最短的匹配可能而不再阻止表达式的剩余部分进行匹配。因此,要让前面的正则表达式正确工作,我们需要把它改为这样。
boost::regex reg("(.*)(//d{2})");如果我们用这个正则表达式来修改程序,那么m[1].matched和m[2].matched都会为true. 表达式.*只消耗最少可能的输入,即它将在第一个字符3处停止,因为这就是表达式要成功匹配所需要的。因此,第一个子表达式会匹配 "Note that I'm" 而第二个匹配 "31".
看一下regex_iterator
我们已经看过如何用几次regex_search调用来处理所有输入,但另一方面,更为优雅的方法是使用regex_iterator. 这个迭代器类型用一个序列来列举正则表达式的所有匹配。解引用一个regex_iterator会产生对一个match_results实例的引用。构造一个regex_iterator时,你要把指示输入序列的迭代器传给它,并提供相应的正则表达式。我们来看一个例子,输入数据是一组由逗号分隔的整数。相应的正则表达式很简单。
boost::regex reg("(//d+),");在正则表达式的最后加一个(匹配零次或一次) 确保最后一个数字可以被成功分析,即使输入序列不是以逗号结束。另外,我们还使用了另一个重复符+. 这个重复符表示匹配一次或多次。现在,不需要多次调用regex_search, 我们创建一个regex_iterator, 并调用算法for_each, 传给它一个函数对象,该函数对象以迭代器的解引用进行调用。下面是一个接受任意形式的match_results的函数对象,它有一个泛型的调用操作符。它所执行的就是把当前匹配的值加到一个总和中(在我们的正则表达式中,第一个子表达式是我们要用的)。
class regex_callback {
int sum_;
public:
regex_callback() : sum_(0) {}
template
void operator()(const T& what) {
sum_+=atoi(what[1].str().c_str());
}
int sum() const {
return sum_;
}
};
现在把这个函数对象的一个实例传递给std::for_each, 结果是对每一个迭代器it的解引用调用该函数对象,即对每一次匹配的子表达式进行调用。
int main() {
boost::regex reg("(//d+),");
std::string s="1,1,2,3,5,8,13,21";
boost::sregex_iterator it(s.begin(),s.end(),reg);
boost::sregex_iterator end;
regex_callback c;
int sum=for_each(it,end,c).sum();
}
如你所见,传递给for_each的end迭代器是regex_iterator一个缺省构造实例。it和end的类型均为boost::sregex_iterator, 即为regex_iterator
的typedef. 这种使用regex_iterator的方法要比我们前面试过的多次匹配的方法更清晰,在多次匹配的方法中我们不得不在一个循环中让起始迭代器不断地前进并调用regex_search。
用regex_token_iterator分割字符串
另一个迭代器类型,或者说得更准确些,迭代器适配器,就是boost::regex_token_iterator. 它与regex_iterator很类似,但却是用于列举不匹配某个正则表达式的每一个字符序列,这对于分割字符串很有用。它也可以用于选择对哪一个子表达式感兴趣,当解引用regex_token_iterator时,只有预订的那个子表达式被返回。考虑这样一个应用程序,它接受一些用斜线号分隔的数据项作为输入。两个斜线号之间的数据组成应用程序要处理的项。使用regex_token_iterator来分割这个字符串很容易。该正则表达式很简单。
boost::regex reg("/");这个 regex 匹配各项间的分割符。要用它来分割输入,只需简单地把指定的索引1传递给regex_token_iterator的构造函数。以下是完整的程序:
int main() {
boost::regex reg("/");
std::string s="Split/Values/Separated/By/Slashes,";
std::vector
vec;
boost::sregex_token_iterator it(s.begin(),s.end(),reg,-1);
boost::sregex_token_iterator end;
while (it!=end)
vec.push_back(*it++);
assert(vec.size()==std::count(s.begin(),s.end(),'/')+1);
assert(vec[0]=="Split");
}
就象regex_iterator一样,regex_token_iterator是一个模板类,它使用所包装的序列的迭代器类型来进行特化。这里,我们用的是sregex_token_iterator, 它是regex_token_iterator
的typedef。每一次解引用这个迭代器it,它返回当前的sub_match, 当这个迭代器前进时,它尝试再次匹配该正则表达式。这两个迭代器类型,regex_iterator和regex_token_iterator, 都非常有用;你应该明白,当你考虑反复调用regex_search时,就该用它们了。
更多的正则表达式
你已经看到了不少正则表达式的语法,但还有更多的要了解。这一节简单地示范一些你每天都会使用的正则表达式的其它功能。作为开始,我们先看一下一组完整的重复符;我们之前已经看到了*,+, 以及使用{}进行限定重复。还有一个重复符,即是. 你可能已经留意到它也可以用于声明非贪婪的重复,但对于它本身而言,它是表示一个表达式必须出现零次或一次。还有一点值得提及的是,限定重复符可以很灵活;下面是三种不同的用法:
boost::regex reg1("//d{5}");
boost::regex reg2("//d{2,4}");
boost::regex reg3("//d{2,}");
第一个正则表达式匹配5个数字。第二个匹配 2个, 3个, 或者 4个数字。第三个匹配2个或更多个数字,没有上限。
另一种重要的正则表达式特性是使用元字符^表示非字符类别。用它来表示一个匹配任意不在给定字符类别中的字符;即你所列字符类别的补集。例如,看如下正则表达式。
boost::regex reg("[^13579]");它包含一个非字符类别,匹配任意不是奇数数字的字符。看一下以下这个小程序,试着给出程序的输出。
int main() {
boost::regex reg4("[^13579]");
std::string s="0123456789";
boost::sregex_iterator it(s.begin(),s.end(),reg4);
boost::sregex_iterator end;
while (it!=end)
std::cout << *it++;
}你给出答案了吗?输出是 "02468",即所有偶数数字。注意,这个字符类别不仅匹配偶数数字,如果输入字符串是 "AlfaBetaGamma",那么也会全部匹配。
我们看到的这个元字符,^, 还有另一个意思。它可以用来表示一行的开始。而元字符$则表示一行的结束。
错的正则表达式
一个错的正则表达式就是一个不遵守规则的正则表达式。例如,你可能忘了一个右括号,这样正则表达式引擎将无法成功编译这个正则表达式。这时,将抛出一个bad_expression类型的异常。正如我前面提到的,这个异常的名字将会在下一版本的Boost.Regex中被修改,还有在即将加入Library Technical Report的版本中也是。异常类型bad_expression将被更名为regex_error.
如果你的应用程序中的正则表达式全都是硬编码的,你可能不用处理错误表达式,但如果你是接受了用户的输入来作为正 则表达式,你就必须准备进行错误处理。这里有一个程序,提示用户输入一个正则表达式,接着输入一个用来对正则表达式进行匹配的字符串。由用户进行输入时, 总是有可能会导致无效的输入。
int main() {
std::cout << "Enter a regular expression:/n";
std::string s;
std::getline(std::cin, s);
try {
boost::regex reg(s);
std::cout << "Enter a string to be matched:/n";
std::getline(std::cin,s);
if (boost::regex_match(s,reg))
std::cout << "That's right!/n";
else
std::cout << "No, sorry, that doesn't match./n";
}
catch(const boost::bad_expression& e) {
std::cout <<
"That's not a valid regular expression! (Error: " <<
e.what() << ") Exiting.../n";
}
}
为了保护应用程序和用户,一个try/catch块用于处理构造时抛出boost::regex的情形,这时会打印一个提示信息,而程序会温和地退出。用这个程序来测试,我们开始时输入一些合理的数据。
Enter a regular expression:
/d{5}
Enter a string to be matched:
12345
That's right!现在,给一些错误的数据,试着输入一个错误的正则表达式。
Enter a regular expression:
(/w*))
That's not a valid regular expression! (Error: Unmatched ( or /() Exiting...
在regex reg构造时,就会抛出一个异常,因为这个正则表达式不能被编译。因此,进入到catch的处理例程中,程序将打印一个错误信息并退出。你只需知道有三个可能会发生异常的地方。一个是在构造一个正则表达式时,就象你刚刚看到的那样;另一个是使用成员函数assign把正则表达式赋给一个regex时。最后一个是,regex迭代器和算法也可能抛出异常,如果内存不够或者匹配的复杂度过快增长的话。
Regex 总结
无可争议,正则表达式是非常有用和重要的,而本库给C++带来了强大的正则表达式功能。传统上,用户除了使用 POSIX C API来实现正则表达式功能以外,别无选择。对于文本处理的验证工作,正则表达式比手工编写分析代码要灵活和可靠得多。对于查找和替换,使用正则表达式可 以优美地解决很多相关问题,而不用它们则根本无法解决。
Boost.Regex 是一个强大的库,因此不可能在这一章中完全覆盖它所有的内容。同样,正则表达式的完美表现 和广泛的应用范围意味着本章也不仅仅是简单地介绍一下它们。这个主题可以写成一本单独的书。要知道更多,可以学习Boost.Regex的在线文档,并且 找一本关于正则表达式的书(考虑一下参考书目中的建议)。不论Boost.Regex有多强大,正则表达式有多广多深,初学者还是可以有效地使用本库中的 正则表达式。对于那些由于C++不支持正则表达式而选择了其它语言的程序员,欢迎你们回家。
Boost.Regex并不是C++程序员唯一可以使用的正则表达式库,但它的确是最好的一个。它易于使用,并且在匹配你的正则表达式时快如闪电。你应该尽可能去用它。
Boost.Regex 的作者是 Dr. John Maddock.
一个经典的boost regex的例子代码
一个网上经典的例子。
#include "stdafx.h"
#include
#include
#include
#include
#include
using namespace std;
using namespace boost;
regex expression("^select ([a-zA-Z]*) from ([a-zA-Z]*)");
int main(int argc, char* argv[])
{
std::string in;
cmatch what;
cout << "enter test string" << endl;
getline(cin,in);
if(regex_match(in.c_str(), what, expression))
{
for(int i=0;i
cout<<"str :"<
.str()<
}
else
{
cout<<"Error Input"<
}
return 0;
}
==============
结果
输入:select name from table
输出:str:select name from table
str:name
str:table
按照我们的要求,字符串被匹配挑出来了。
C++ boost 正则库regex的一些基本用法的例子
#include
#include
#include
using namespace std;
using namespace boost;
/*搜索字符串中是否含有子字符串
int main( int argc, char* argv[] )
{
char *buf = "This is boost::regex example boost::regex";
boost::regex exampleregex( "boost::regex" );
boost::cmatch result;
if( boost::regex_search( buf, result, exampleregex ) )
{
std::cout << result.str() << std::endl;
}
//boost::regex_replace(buf,)
return 0;
}*/
//写了个去除左侧无效字符(空格,回车,TAB)的正则表达式。
/*void main()
{
std::string testString = " /r/n Hello World ! GoodBye World/r/n";
std::string TrimLeft = "([//s//r//n//t]*)(//w*.*)";
boost::regex expression(TrimLeft);
testString = boost::regex_replace( testString, expression, "$2" );
std::cout<< "TrimLeft:" << testString <
}*/
//关于重复的贪婪
/*void main()
{
std::string regstr = "(.*?)(age)(.*?)(//d{2})";
boost::regex expression(regstr);
std::string testString = "My age is 28 His age is 27";
boost::smatch what;
std::string::const_iterator start = testString.begin();
std::string::const_iterator end = testString.end();
while( boost::regex_search(start, end, what, expression) )
{
std::string name(what[1].first, what[1].second);
std::string age(what[4].first, what[4].second);
std::cout<< "Name:" << name.c_str() << std::endl;
std::cout<< "Age:" < << std::endl;
start = what[0].second;
}
}*/
//regex_search学习
//regex_search与regex_match基本相同,只不过regex_search不要求全部匹配,即部份匹配(查找)即可。
//检测字符串里是否含有数字
//简单例子:
/*void main()
{
std::string regstr = "(//d+)";
boost::regex expression(regstr);
std::string testString = "192.168.4.1";
boost::smatch what;
if( boost::regex_search(testString, expression) )
{
std::cout<< "Have digit" << std::endl;
}
}
//用于打印出所有的数字
void main()
{
std::string regstr = "(//d+)";
regstr = "(?<=()(.+?)(?=))";
//regstr = "((?=.+))";
boost::regex expression(regstr);
std::string testString = "sdaaccfsaaccdf192dfsdbbdd.168sdfs.sdfs4dsf.sfdsd1asfscvasdf";
boost::smatch what;
std::string::const_iterator start = testString.begin();
std::string::const_iterator end = testString.end();
while( boost::regex_search(start, end, what, expression) )
{
std::cout<< "Have digit:" ;
std::string msg(what[0].first, what[0].second);
std::cout<< msg.c_str() << std::endl;
start = what[0].second;
}
}
*/
//#include "stdafx.h"
#include
#include
#include
#include
#include
using namespace std;
//using namespace boost;
boost::wregex expression(L"^//s*我+//s*[想|爱|恨|扁]+//s*你");
int main(int argc, char* argv[])
{
locale loc( "Chinese-simplified" );
wcout.imbue(loc);
std::wstring in = L"我我我我 爱爱爱爱爱 你";
static boost::wsmatch what;
cout << "enter test string" << endl;
//getline(cin,in);
if(boost::regex_match(in.c_str(), what, expression))
{
for(int i=0;i
wcout<
}
else
{
wcout<
}
return 0;
}
/*
#include
#include
#include
int main() {
std::string str = "I'm singing while you're dancing.";
std::string pattern = "(//b//w+(?=ing//b))";
boost::regex ee_all(pattern);
boost::smatch result;
std::string::const_iterator it = str.begin();
std::string::const_iterator end = str.end();
while(boost::regex_search(it, end, result, ee_all)) {
std::cout << result[0] << '/n';
it = result[0].second;
}
}
*/
/*void main()
{
std::string testString = "sdfsaaccdf192dfsdbbdd.168sdfs.sdfs4dsf.sfdsd1asfscvasdf";
cmatch what;
regex expression( "(?=(.+?))");
if(regex_match(testString,what,expression))
{
int sun = what.size();
cout< <
for(int i=0;i
cout << "str: " < <
}
else
{
cout << "Error "<
}
}*/
/*
四:regex_match例子代码学习
1 我们经常会看一个字符串是不是合法的IP地址,合法的IP地址需要符合以下这个特征:
xxx.xxx.xxx.xxx 其中xxx是不超过255的整数
正则表达式找到上面的这种形式的字符串相当容易,只是判断xxx是否超过255就比较困难了(因为正则表达式是处理的文本,而非数字)
OK,我们先来处理一个数字,即:xxx。找到一种表达式来处理这个数字,并且保证这个数字不会超过255
第一种情况:x,即只有一个数字,它可以是0~9 ,用/d 表示
第二种情况:xx,即有两个数字,它可以是00~99,用/d/d 表示
第三种情况:xxx,这种情况分为两种,一种是 1xx,可以用 1/d/d 表示
另外一种是 2xx,这又分为两种 2[1234]/d
和 25[12345]
好了组合起来
1?/d{1,2}|2[1234]/d|25[12345]
既可以标识一个不大于255的数字字符串
嗯,我们现在需要重复这种情况既可:
(1?/d{1,2}|2[1234]/d|25[12345])/.(1?/d{1,2}|2[1234]/d|25[12345])/.(1?/d{1,2}|2[1234]/d|25[12345])/.(1?/d{1,2}|2[1234]/d|25[12345])
呵呵,长是长了点,我试图用boost支持的子表达式缩短,但是没有达到效果,请各位了解boost的正则表达式的达人指点:
(1?/d{1,2}|2[1234]/d|25[12345])/./1$/./1$/./1$
(参看反向索引:[url]http://www.boost.org/libs/regex/doc/syntax_perl.html
boost::lexical_cast进行万能的格式转换
boost::lexical_cast为数值之间的转换(conversion)提供了一揽子方案,比如:将一个字符串"123"转换成整数123,代码如下:
- string s = "123";
- int a = lexical_cast<int>(s);
这种方法非常简单,笔者强烈建议大家忘掉std诸多的函数,直接使用boost:: lexical_cast。如果转换发生了意外,lexical_cast会抛出一个bad_lexical_cast异常,因此程序中需要对其进行捕捉。
现在动手
编写如下程序,体验如何使用boost:: lexical_cast完成数值转换。
【程序 4-11】使用boost:: lexical_cast完成对象数值转换
- 01 #include "stdafx.h"
- 02
- 03 #include
- 04 #include
- 05
- 06 using namespace std;
- 07 using namespace boost;
- 08
- 09 int main()
- 10 {
- 11 string s = "123";
- 12 int a = lexical_cast<int>(s);
- 13 double b = lexical_cast<double>(s);
- 14
- 15 printf("%d/r/n", a + 1);
- 16 printf("%lf/r/n", b + 1);
- 17
- 18 try
- 19 {
- 20 int c = lexical_cast<int>("wrong number");
- 21 }
- 22 catch(bad_lexical_cast & e)
- 23 {
- 24 printf("%s/r/n", e.what());
- 25 }
- 26
- 27 return 0;28 }
字符串→数值
使用Boost Conversion Library所提供的函数lexical_cast(需要引入头文件boost/lexical_cast.hpp)无疑是最简单方便的。如:
#include
#include
int main()
{
using boost::lexical_cast;
int a = lexical_cast
( "123 ");
double b = lexical_cast
( "123.12 ");
std::cout <
<
std::cout <
<
return 0;
}
一个函数就简洁地解决了所有的问题。
数值→字符串
#include
#include
#include
int main()
{
using std::string;
const double d = 123.12;
string s = boost::lexical_cast
(d);
std::cout <
<
return 0;
}
跟前面一样简单。
异常
如果转换失败,则会有异常bad_lexical_cast抛出。该异常类是标准异常类bad_cast的子类。
#include
#include
int main()
{
using std::cout;
using std::endl;
int i;
try{
i = boost::lexical_cast
( "abcd ");
}
catch(boost::bad_lexical_cast& e)
{
cout <
<
return 1;
}
cout <
<
return 0;
}
显然“abcd”并不能转换为一个int类型的数值,于是抛出异常,捕捉后输出“bad lexical cast: source type value could not be interpreted as target”这样的信息。
由于Visual C++ 6的本地化(locale)部分实现有问题,因此如果使用了非默认的locale,可能会莫名其妙地抛出异常。当然,一般情况下我们并不需要去改变默认的locale,所以问题不是很大。
输入数据必须“完整”地转换,否则抛出bad_lexical_cast异常。例如
int i = boost::lexical_cast
( "123.123 "); // this will throw
便会抛出异常。因为“123.123”只能“部分”地转换为123,不能“完整”地转换为123.123。
浮点数的精度问题。
std::string s = boost::lexical_cast
(123.1234567);
以上语句预想的结果是得到“123.1234567”,但是实际上我们只会得到“123.123”,因为默认情况下std::stringstream的精度是6(这是C语言程序库中的“前辈”printf留下的传统)。这可以说是boost::lexical_cast的一个bug。怎么办呢?权宜之计,可以这么做:打开头文件
,注意对照修改[6]:
#include
//...
template
Target lexical_cast(Source arg) {
//...
Target result;
interpreter.precision(std::numeric_limits
if( !(interpreter < < arg) ||
!(interpreter > > result) ||
!(interpreter > > std::ws).eof())
//...
}
即可得到正确结果。当然,理论上效率会有一点点损失,不过几乎可以忽略不计。