boost库中的正则表达式文档格式.docx
《boost库中的正则表达式文档格式.docx》由会员分享,可在线阅读,更多相关《boost库中的正则表达式文档格式.docx(17页珍藏版)》请在冰点文库上搜索。
unsignedmark_count()const;
mark_count返回regex中带标记子表达式的数量。
带标记子表达式是指正则表达式中用圆括号括起来的部分。
匹配这个子表达式的文本可以通过调用某个正则表达式算法而获得。
flag_typeflags()const;
返回一个位掩码,其中包含这个basic_regex所设置的选项标志。
例如标志icase,表示正则表达式忽略大小写,标志JavaScript,表示regex使用JavaScript的语法。
typedefbasic_regex<
不要使用类型basic_regex来定义变量,你应该使用这两个typedef中的一个。
这两个类型,regex和wregex,是两种字符类型的缩写,就如string和wstring是basic_string<
和basic_string<
的缩写一样。
这种相似性是不一样的,某种程度上,regex是一个特定类型的字符串的容器。
普通函数template<
classcharT,classAllocator,classtraits>
boolregex_match(constcharT*str,match_results<
constcharT*,Allocator>
&
m,constbasic_regex<
charT,traits>
e,match_flag_typeflags=match_default);
regex_match判断一个正则表达式(参数e)是否匹配整个字符序列str.它主要用于验证文本。
注意,这个正则表达式必须匹配被分析串的全部,否则函数返回false.如果整个序列被成功匹配,regex_match返回True.
template<
classcharT,classAllocator,classtraits>
boolregex_search(constcharT*str,match_results<
regex_search类似于regex_match,但它不要求整个字符序列完全匹配。
你可以用regex_search来查找输入中的一个子序列,该子序列匹配正则表达式e.
classtraits,classcharT>
basic_string<
regex_replace(constbasic_string<
s,constbasic_regex<
e,constbasic_string<
fmt,match_flag_typeflags=match_default);
regex_replace在整个字符序列中查找正则表达式e的所有匹配。
这个算法每次成功匹配后,就根据参数fmt对匹配字符串进行格式化。
缺省情况下,不匹配的文本不会被修改,即文本会被输出但没有改变。
这三个算法都有几个不同的重载形式:
一个接受constcharT*(charT为字符类型),另一个接受constbasic_string<
还有一个重载接受两个双向迭代器作为输入参数。
用法
要使用Boost.Regex,你需要包含头文件"
.Regex是本书中两个需要独立编译的库之一(另一个是Boost.Signals)。
你会很高兴获知如果你已经构建了Boost——那只需在命令提示符下打一行命令——就可以自动链接了(对于Windows下的编译器),所以你不需要为指出那些库文件要用而费心。
你要做的第一件事就是声明一个类型basic_regex的变量。
这是该库的核心类之一,也是存放正则表达式的地方。
创建这样一个变量很简单;
只要将一个含有你要用的正则表达式的字符串传递给构造函数就行了。
boost:
regexreg("
(A.*)"
);
这个正则表达式具有三个有趣的特性。
第一个是,用圆括号把一个子表达式括起来,这样可以稍后在同一个正则表达式中引用它,或者取出匹配它的文本。
我们稍后会详细讨论它,所以如果你还不知道它有什么用也不必担心。
第二个是,通配符(wildcard)字符,点。
这个通配符在正则表达式中有非常特殊的意义;
这可以匹配任意字符。
最后一个是,这个表达式用到了一个重复符,*,称为Kleenestar,表示它前面的表达式可以被匹配零次或多次。
这个正则表达式已可以用于某个算法了,如下:
boolb=boost:
regex_match("
ThisexpressioncouldmatchfromAandbeyond."
reg);
如你所见,你把正则表达式和要分析的字符串传递给算法regex_match.如果的确存在与正则表达式的匹配,则该函数调用返回结果true;
否则,返回false.在这个例子中,结果是false,因为regex_match仅当整个输入数据被正则表达式成功匹配时才返回true。
你知道为什么是这样吗?
再看一下那个正则表达式。
第一个字符是大写的A,很明显能够匹配这个表达式的第一个字符在哪。
所以,输入的一部分"
Aandbeyond."
可以匹配这个表达式,但这不是整个输入。
让我们试一下另一个输入字符串。
AsthisstringstartswithA,doesitmatch?
这一次,regex_match返回true.当正则表达式引擎匹配了A,它接着看后续有什么。
在我们的regex变量中,A后跟一个通配符和一个Kleenestar,这意味着任意字符可以被匹配任意次。
因而,分析过程开始扔掉输入字符串的剩余部分,即匹配了输入的所有部分。
接下来,我们看看如何使用regexes和regex_match来进行数据验证。
验证输入
正则表达式常用于对输入数据的格式进行验证。
应用软件通常要求输入符合某种结构。
考虑一个应用软件,它要求输入一定要符合如下格式,"
3个数字,一个单词,任意字符,2个数字或字符串"
N/A,"
一个空格,然后重复第一个单词."
手工编写代码来验证这个输入既沉闷又容易出错,而且这些格式还很可能会改变;
在你弄明白之前,可能就需要支持其它的格式,你精心编写的分析器可能就需要修改并重新调试。
让我们写出一个可以验证这个输入的正则表达式。
首先,我们需要一个匹配3个数字的表达式。
对于数字,我们应该使用一个特别的缩写,\d。
要表示它被重复3次,需要一个称为boundsoperator的特定重复,它用花括号括起来。
把这两个合起来,就是我们的正则表达式的开始部分了。
\\d{3}"
注意,我们需要在转义字符(\)之前加一个转义字符,即在我们的字符串中,缩写\d变成了\\d。
这是因为编译器会把第一个\当成转义字符扔掉;
我们需要对\进行转义,这样\才可以出现在我们的正则表达式中。
接下来,我们需要定义一个单词的方法,即定义一个字符序列,该序列结束于一个非字母字符。
有不只一种方法可以实现它,我们将使用字符类别(也称为字符集)和范围这两个正则表达式的特性来做。
字符类别即一个用方括号括起来的表达式。
例如,一个匹配字符a,b,和c中任一个的字符类别表示为:
[abc].如果用范围来表示同样的东西,我们要写:
[a-c].要写一个包含所有字母的字符类型,我们可能会有点发疯,如果要把它写成:
[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ],但不用这样;
我们可以用范围来表示:
[a-zA-Z].要注意的是,象这样使用范围要依赖于当前所用的locale,如果正则表达式的basic_regex:
collate标志被打开。
使用以上工具以及重复符+,它表示前面的表达式可以重复,但至少重复一次,我们现在可以表示一个单词了。
[a-zA-Z]+"
以上正则表达式可以工作,但由于经常要表示一个单词,所以有一个更简单的方法:
\w.这个符号匹配所有单词,不仅是ASCII的单词,因此它不仅更短,而且也更适用于国际化的环境。
接下来的字符是一个任意字符,我们已经知道要用点来表示。
."
再接下来是2个数字或字符串"
N/A."
为了匹配它,我们需要用到一个称为选择的特性。
选择即是匹配两个或更多子表达式中的任意一个,每种选择之间用|分隔开。
就象这样:
(\\d{2}|N/A)"
注意,这个表达式被圆括号括了起来,以确保整个表达式被看作为两个选择。
在正则表达式中增加一个空格是很简单的;
用缩写\s.把以上每一样东西合并起来,就得到了以下表达式:
\\d{3}[a-zA-Z]+.(\\d{2}|N/A)\\s"
现在事情变得有点复杂了。
我们需要某种方法,来验证接下来的输入数据中的单词是否匹配第一个单词(即那个我们用表达式[a-zA-Z]+所捕获的单词)。
关键是要使用后向引用(backreference),即对前面的子表达式的引用。
为了可以引用表达式[a-zA-Z]+,我们必须先把它用圆括号括起来。
这使得表达式([a-zA-Z]+)成为我们的正则表达式中的第一个子表达式,我们就可以用索引1来建立一个后向引用了。
这样,我们就得到了整个正则表达式,用于表示"
:
\\d{3}([a-zA-Z]+).(\\d{2}|N/A)\\s\\1"
干的好!
下面是一个简单的程序,把这个表达式用于算法regex_match,验证两个输入字符串。
#include<
iostream>
#include<
cassert>
string>
#include"
intmain(){//3digits,aword,anycharacter,2digitsor"
N/A"
//aspace,thenthefirstwordagainboost:
std:
stringcorrect="
123HelloN/AHello"
;
stringincorrect="
123Hello12hello"
assert(boost:
regex_match(correct,reg)==true);
regex_match(incorrect,reg)==false);
}
第一个字符串,123HelloN/AHello,是正确的;
123是3个数字,Hello是一个后跟任意字符(一个空格)的单词,然后是N/A和另一个空格,最后重复单词Hello。
第二个字符串是不正确的,因为单词Hello没有被严格重复。
缺省情况下,正则表达式是大小写敏感的,因而反向引用不能匹配。
写出正则表达式的一个关键是成功地分解问题。
看一下你刚才建立的最终的那个表达式,对于未经过训练的人来说它的确很难懂。
但是,如果把这个表达式分解成小的部分,它就不太复杂了。
查找
现在我们来看一下另一个Boost.Regex算法,regex_search.与regex_match不同的是,regex_search不要求整个输入数据完全匹配,则仅要求部分数据可以匹配。
作为说明,考虑一个程序员的问题,他可能在他的程序中有一至两次忘记了调用delete。
虽然他知道这个简单的测试可能没什么意义,他还是决定计算一下new和delete出现的次数,看看数字是否符合。
这个正则表达式很简单;
我们有两个选择,new和delete.
(new)|(delete)"
有两个原因我们要把子表达式用括号括起来:
一个是为了表明我们的选择是两个组。
另一个原因是我们想在调用regex_search时引用这些子表达式,这样我们就可以判断是哪一个选择被匹配了。
我们使用regex_search的一个重载,它接受一个match_results类型的参数。
当regex_search执行匹配时,它通过一个match_results类型的对象报告匹配的子表达式。
类模板match_results使用一个输入序列所用的迭代器类型来参数化。
classIterator,classAllocator=std:
allocator<
sub_match<
Iterator>
classmatch_results;
typedefmatch_results<
constchar*>
cmatch;
constwchar_t>
wcmatch;
std:
string:
const_iterator>
smatch;
wstring:
wsmatch;
我们将使用std:
string,所以要留意typedefsmatch,它是match_results<
的缩写。
如果regex_search返回true,传递给该函数的match_results引用将包含匹配的子表达式结果。
在match_results里,用已索引的sub_match来表示正则表达式中的每个子表达式。
我们来看一下我们如何帮助这位困惑的程序员来计算对new和delete的调用。
boost:
smatchm;
strings="
Callstonewmustbefollowedbydelete.\Callingsimplynewresultsinaleak!
"
if(boost:
regex_search(s,m,reg)){//Didnewmatch?
if(m[1].matched)std:
cout<
<
Theexpression_r(new)matched!
\n"
if(m[2].matched)std:
Theexpression_r(delete)matched!
以上程序在输入字符串中查找new或delete,并报告哪一个先被找到。
通过传递一个类型smatch的对象给regex_search,我们可以得知算法如何执行成功的细节。
我们的表达式中有两个子表达式,因此我们可以通过match_results的索引1得到子表达式new.这样我们得到一个sub_match实例,它有一个Boolean成员,matched,告诉我们这个子表达式是否参与了匹配。
因此,对于上例的输入,运行结果将输出"
.现在,你还有一些工作要做。
你需要继续把正则表达式应用于输入的剩余部分,为此,你要使用另外一个regex_search的重载,它接受两个迭代器,指示出要查找的字符序列。
因为std:
string是一个容器,它提供了迭代器。
现在,在每一次匹配时,你必须把指示范围起始点的迭代器更新为上一次匹配的结束点。
最后,增加两个变量来记录new和delete的次数。
以下是完整的程序:
intmain(){//"
new"
and"
delete"
出现的次数是否一样?
intnew_counter=0;
intdelete_counter=0;
const_iteratorit=s.begin();
const_iteratorend=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:
Leakdetected!
elsestd:
Seemsok...\n"
注意,这个程序总是把迭代器it设置为m[0].second。
match_results[0]返回对匹配整个正则表达式的子匹配的引用,因此我们可以确认这个匹配的结束点就是下次运行regex_search的起始点。
运行这个程序将输出"
因为这里有两次new,而只有一次delete.当然,一个变量也可能在两个地方删除,还有可能调用new[]和delete[],等等。
现在,你应该已经对子表达式如何分组使用有了较好的了解。
现在是时候进入到最后一个Boost.Regex算法,该算法用于执行替换工作。
替换
Regex算法家族中的第三个算法是regex_replace.顾名思义,它是用于执行文本替换的。
它在整个输入数据中进行搜索,查找正则表达式的所有匹配。
对于表达式的每一个匹配,该算法调用match_results:
format并输入结果到一个传入函数的输出迭代器。
在本章的介绍部分,我给出了一个例子,将英式拼法的colour替换为美式拼法color.不使用正则表达式来进行这个拼写更改会非常乏味,也很容易出错。
问题是可能存在不同的大小写,而且会有很多单词被影响,如colourize.要正确地解决这个问题,我们需要把正则表达式分为三个子表达式。
(Colo)(u)(r)"
boost:
regex:
icase|boost:
perl);
我们将要去掉的字母u独立开,为了在所有匹配中可以很容易地删掉它。
另外,注意到这个正则表达式是大小写无关的,我们要把格式标志boost:
icase传给regex的构造函数。
你还要传递你想要设置的其它标志。
设置标志时一个常见的错误就是忽略了regex缺省打开的那些标志,如果你没有设置这些标志,它们不会打开,你必须设置所有你要打开的标志。
调用regex_replace时,我们要以参数方式提供一个格式化字符串。
该格式化字符串决定如何进行替换。
在这个格式化字符串中,你可以引用匹配的子表达式,这正是我们想要的。
你想保留第一个和第三个匹配的子表达式,而去掉第二个(u)。
表达式$N表示匹配的子表达式,N为子表达式索引。
因此我们的格式化串应该是"
$1$3"
表示替换文本为第一个和第三个子表达式。
通过引用匹配的子表达式,我们可以保留匹配文本中的所有大小写,而如果我们用字符串来作替换文本则不能做到这一点。
以下是解决这个问题的完整程序。
intmain(){boost:
strings="
Colour,colours,color,colourize"
s=boost:
regex_replace(s,reg,"
s;
程序的输出是"
Color,colors,color,colorize"
.regex_replace对于这样的文本替换非常有用。
用户常见的误解
我所见到的与Boost.Regex相关的最常见的问题与regex_match的语义有关。
人们很容易忘记必须使regex_match的所有输入匹配给定的正则表达式。
因此,用户常以为以下代码会返回true.
\\d*"
boolb=boost