boost::regex的默认正则表达式语法是perl语法
boost::regex支持perl regular表达式、POSIX-Extended regular表达式和POSIX-Basic Regular表达式,但默认的表达式语法是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正则表达式语法
perl正则表达式语法可参见《perl语言入门》第7、8、9章或boost的文档。这里列出的语法是不全面的,而且部分说明可能并不清楚。
. 任意字符;使用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使用ICU来实现对unicode及unicode变种的支持,这需要在编译boost的时候指出是否使用ICU以及ICU所在的目录。否则编译出来的boost::regex不支持unicode编码。其中boost::wregex支持unicode编码的搜索,如果要搜索UTF-8、UTF-16、UFT-32编码的字符串,则要用boost::u32regex。注意boost::wregex只能支持unicode编码,不能支持uft编码。
搜索时如何忽略大小写
如果要在搜索时忽略大小写(即大小写不敏感),则要用到表达式选项boost::regex::icase,例如: boost::regex e2(my_expression, boost::regex::perl|boost::regex::icase);
模板类:
l basic_regex 用来保存一个“正则表达式”的类。
l sub_match 继承于pair<Iterator,Iterator>迭代器组,用来表示匹配的一个结果。
l match_results sub_match的容器,用来表示一次搜索或匹配算法的所有结果,类似于vector<sub_match>。
算法:
l regex_math 匹配算法,测试一个字符串是否和一个正则式匹配,并通过match_results返回结果。
l regex_find 查找算法,查找字符串的一个和正则式匹配的字串,并通过match_results返回结果。
l regex_format 替换算法,查找字符串中的所有匹配正则式的字串,并使用“格式化字符”串替换。
迭代器:
l regex_iterator 枚举一个字符串中所有匹配的字串,regex_iterator的结果相当于match_results。
l regex_token_iterator 枚举一个字符串中所有匹配的字串,regex_iterator的结果相当于sub_match。
详述
l basic_regex
template <class charT, class traits = regex_traits<charT>, class Allocator = std::allocator<charT> >
class basic_regex;
typedef basic_regex<char> regex;
typedef basic_regex<wchar_t> wregex;
很明显,charT是正则式的字符类型,regex和wregex是basic_regex的两个特化。
注意,正则式的字符类型要和需要匹配的字符串的字符类型相同。例如:不能在regex_find算法中分别使用string和wregex最为参数,要么是string和regex,要么是wstring和wregex。
构造函数:
basic_regex re | 产生空的正则式 |
basic_regex re(str) | 正则式为str,str可以为basic_string,也可以是0结尾的char*字符串。 |
Basic_regex re(re2) | 拷贝构造。 |
basic_regex re(str,flag) | 正则式为str,使用flag语法选项,flag是一组常量的组合。例如:icase可以使正则式匹配忽略大小写。 |
basic_regex re(beg,end) | 使用迭代器构造正则式。可以是basic_string的迭代器,也可以是const char*。 |
basic_regex re(beg,end,flag) | 使用迭代器构造正则式,flag是语法选项。 |
常用的语法选项:
regex_constants::normal | 默认的语法。符合EMCAScript,JavaScript中的正则式。 |
regex_constants::icase | 匹配的时候忽略大小写。 |
regex_constants::nosubs | 不把匹配的子串保存进match_results结构。 |
regex_constants::collate | 对于[a-b]的匹配,考虑地区 |
语法选项通过或运算来结合。在basic_regex中这些语法选项也进行了定义,所以可以写成regex::normal,这要比regex_constants少打好几个字母了吧!J
assign成员函数:
re.assign(re2) | 复制一个正则式 |
re.assign(str) | 正则式为str。 |
re.assign(str, flag) | 正则式为str,使用flag语法选项,flag是一组常量的组合。 |
re.assign(beg, end) | 使用迭代器构造正则式。 |
re.assign(beg, end, flag) | 使用迭代器构造正则式,flag是语法选项。 |
其实basic_regex很多用法和basic_string很像,因为正则表达式也是个字符串嘛!
迭代器:
regex::iterator it | 常迭代器类型,即const_iterator |
re.begin() | 返回的是常迭代器哦!const_iterator |
re.end() | 没有逆向迭代器。 |
例如:copy(re.begin(), re.end(), ostream_iterator<char>(cout));
其他:
re.size() | 正则表达式长度,即str的长度。 |
re.max_size() | 正则表达式的最大长度。 |
re.empty() | 长度是否为0 |
re.mark_count() | 返回正则式的组数,一般情况下为小括号对数+1。在boost.regex中使用小括号分组,详情请看下面的算法详解。 |
re.flags() | 返回语法选项。 |
cout<<re | 正则式的流输出,相当于上面示例的copy算法。 |
swap | 成员函数,全局函数都有 |
re.imbue(loc) | 设置local为loc,返回原来的local |
re.getloc() | 得到当前local |
==,!=,<,<=,>,>= | 比较运算符重载 |
l sub_match
sub_match是一个迭代器组,表示正则式中的一个匹配。
template <class BidirectionalIterator>
class sub_match : public std::pair<BidirectionalIterator, BidirectionalIterator>;
boost没有提供sub_match的任何特化,因为我们不会显示的声明一个sub_match变量。sub_match是作为match_results的元素用的。比如:match_results的operator[]和迭代器返回的就是一个特化的sub_match。
唯一的成员变量:
bool matched 是否匹配。
成员函数:
length() | 返回长度,即两个迭代器之间的距离。 |
operator basic_string< value_type>() | 隐式的basic_string转换。 |
str() | 显式的basic_string转换。 |
还有就是一大堆的比较操作符的重载了,这里就不多说了。
l match_results
match_results相当于sub_match的容器,用于表示正则式算法的返回结果。
template <class BidirectionalIterator,
class Allocator = allocator<sub_match<BidirectionalIterator> >
class match_results;
typedef match_results<const char*> cmatch;
typedef match_results<const wchar_t*> wcmatch;
typedef match_results<string::const_iterator> smatch;
typedef match_results<wstring::const_iterator> wsmatch;
声明很简单,有四个特化可以直接使用,不过要注意string和char*字符串使用的match_results是不同的。
成员函数:
m.size() | 容量。 |
m.max_size() | 最大容量。 |
m.empty() | 容量是否为0。 |
m[n] | 第n个元素,即sub_match |
m.prefix() | 返回代表前缀的sub_match,前缀指字符串的开头到第一个匹配的开头。 |
m.suffix() | 返回代表后缀的sub_match,后缀之最后一个匹配的结尾到字符串的结尾。 |
m.length(n) | 返回第n个元素的长度,即m[n].size()。 |
m.position(n) | 返回第n个元素的位置。 |
cout<<m | 流输出,输出整个匹配,相当于cout<<m[0]。因为第0个元素是整个匹配,详细情况请看下面的解释。 |
m.format(fmtstr) | 使用格式化字符串,格式化结果,返回字符串 |
m.format(fmtstr,flags) | 使用格式化字符串,格式化结果,返回字符串,flags是格式化选项。 |
m.format(out,fmtstr) | 同上,但是使用输出迭代器输出结果。 |
m.format(out.fmtstr,flags) | 同上,但是使用输出迭代器输出结果。 |
迭代器:
smatch::iterator | 迭代器,常迭代器 |
smatch::const_iterator | 同上 |
m.begin() | 返回常迭代器 |
m.end() | 同上 |
最后,说一个实例
我处理一个文本
实际值/-20.031,-1.896,-2.861,-1,0,0
提取其中的数字
regex exp("/s*实际值/(-?[0-9.]+),(-?[0-9.]+),(-?[0-9.]+),(-?[0-9.]+),(-?[0-9.]+),(-?[0-9.]+)$");
我们通过三个算法来使用正则式,regex_match、regex_search、regex_replace。
regex_match
regex_match算法用来测试一个字符串是否完全匹配正则式。让我们来看一下regex_match的使用:
if (regex_match(str, m, re))
{
...
}
str是一个字符串,可以是string,wstring,char *或者wchar_t *
m是match_results,它通过引用传入参数,来保存匹配的结果,m要和str的类型匹配,可以是smatch,wsmatch,cmatch或wcmatch,用来分别对应string,wstring,char *或者wchar_t*的str。
re就是正则表达式了,一般来说是regex或wregex。
str,m,re的类型如下:
str类型 | m类型 | re类型 |
string | smatch (match_results<string::const_iterator>) | regex (basic_regex<char>) |
wstring | wsmatch (match_results<wstring::const_iterator>) | wregex (basic_regex<wchar_t>) |
char* | cmatch (match_results<const char*>) | regex (basic_regex<char>) |
wchar_t* | wcmatch (match_results<const wchar_t*>) | wregex (basic_regex<wchar_t>) |
函数的返回值表示字符串是否完全匹配正则表达式,当返回true的时候,m保存了匹配的结果;返回false,m未定义。
下面让我们来看一下,当函数返回true的时候,m是怎么样的。
m.size() == re.mark_count()
还记得re.mark_count()返回的是什么吗?在上一篇中说的是re.mark_count()返回的时正则式的“组数”,并没有详细解释。这里我要详细解释一下。
其实,这个“组数”在boost的regex中叫做sub-expression。sub-expression就是在正则式中使用小括号括起来的一部分,正则式本身是一个sub-expression,所以re.mark_count()等于小括号对数+1。
m.prefix()和m.suffix()
这两个返回的是sub_match类型(相当于一个迭代器组)。在regex_match算法中,这两个返回的sub_match都是空的,他们的值如下:(sub_match继承于pair,所以有first和second成员哦)
m.prefix().first == str.begin()
m.prefix().second == str.begin()
m.prefix().matched == false
m.suffix().first == str.end()
m.suffix().second == str.end()
m.suffix().matched == false
因为regex_match是完全匹配,即整个字符串和正则式匹配,所以前缀和后缀都是空的。
m[0]
返回第0个匹配的,由于regex_match是完全匹配,所以
m[0].first == str.begin()
m[0].second == str.end()
m[0].matched == true
m[n] , n<m.size()
返回第n个匹配的sub-expression。
m[n].matched 表示第n个sub-expression是否在字符串中存在。整个regex匹配,但是sub_exp可能匹配的是空的,例如”(a*)”就有可以匹配空。
m[n].first和m[n].second 表示匹配的范围。如果匹配空的话,都为str.end()。
根据我的测试,m[1],m[2],...,m[n]的顺序是按照正则式的左小括号的顺序来的,例如对于正则式”((a)bc)d(efg)”,如果匹配了一个字符串的话(字符串只可能是”abcdefg”),则
m[0] == “abcdefg” (sub_match重载了==运算符使得可以和一个字符串比较)
m[1] == “abc”
m[2] == “a”
m[3] == “efg”
regex_match的其它用法
regex_match(str,re) | 只测试是否匹配,不需要匹配的结果 |
regex_match(beg,end,re) | 输入的是迭代器 |
regex_match(beg,end,m,re) | 注意m的类型为match_results<iterator> |
regex_match(str,m,re,flag) | flag是匹配选项,默认是的regex_constants::match_default |
regex_search
regex_search的用法基本上和regex_match一样。
if (regex_search(str, m, re))
{
...
}
regex_search不要求str完全匹配re,只要str中的一个字串匹配re就可以了。所以,m.prefix()和m.suffix()不一定为空。
regex_search是从左往右匹配,而且尽量匹配长的字串。
简单的例子
std::string regstr = "a+";
boost::regex expression(regstr);
std::string testString = "aaa";
// 匹配至少一个a
if( boost::regex_match(testString, expression) )
{
std::cout<< "Match" << std::endl;
}
else
{
std::cout<< "Not Match" << std::endl;
}
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$
(参看反向索引: http://www.boost.org/libs/regex/doc/syntax_perl.html
似乎反向只能匹配与第一个字符完全一样的字符串,与我们的需求不同)
Example:
boost::regex expression(regstr);
std::string testString = "192.168.4.1";
if( boost::regex_match(testString, expression) )
{
std::cout<< "This is ip address" << std::endl;
}
else
{
std::cout<< "This is not ip address" << std::endl;
}
2 我们来看看regex_match的另外一个函数原型
template <class ST, class SA, class Allocator, class charT, class traits>
bool regex_match(const basic_string<charT, ST, SA>& s,
match_results<typename basic_string<charT, ST, SA>::const_iterator, Allocator>& m,
const basic_regex <charT, traits>& e, match_flag_type flags = match_default);
template <class BidirectionalIterator, class Allocator, class charT, class traits> bool regex_match(BidirectionalIterator first, BidirectionalIterator last, match_results<BidirectionalIterator, Allocator>& m, const basic_regex <charT, traits>& e, match_flag_type flags = match_default);
注意参数m,如果这个函数返回false的话,m无定义。如果返回true的话,m的定义如下
Element | Value |
m.size() | e.mark_count() |
m.empty() | false |
m.prefix().first | first |
m.prefix().last | first |
m.prefix().matched | false |
m.suffix().first | last |
m.suffix().last | last |
m.suffix().matched | false |
m[0].first | first |
m[0].second | last |
m[0].matched |
|
m[n].first | For all integers n < m.size(), the start of the sequence that matched sub-expression n. Alternatively, if sub-expression n did not participate in the match, then last. |
m[n].second | For all integers n < m.size(), the end of the sequence that matched sub-expression n. Alternatively, if sub-expression n did not participate in the match, then last. |
m[n].matched | For all integers n < m.size(), true if sub-expression n participated in the match, false otherwise. |
boost::regex expression(regstr);
std::string testString = "192.168.4.1";
boost::smatch what;
if( boost::regex_match(testString, what, expression) )
{
std::cout<< "This is ip address" << std::endl;
for(int i = 1;i <= 4;i++)
{
std::string msg(what[i].first, what[i].second);
std::cout<< i << ":" << msg.c_str() << std::endl;
}
}
else
{
std::cout<< "This is not ip address" << std::endl;
}
This is ip address
1:192
2:168
3:4
4:1
regex_search学习
regex_search与regex_match基本相同,只不过regex_search不要求全部匹配,即部份匹配(查找)即可。
简单例子:
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;
}
上面这个例子检测给出的字符串中是否包含数字。
好了,再来一个例子,用于打印出所有的数字
boost::regex expression(regstr);
std::string testString = "192.168.4.1";
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[1].first, what[1].second);
std::cout<< msg.c_str() << std::endl;
start = what[0].second;
}
打印出:
Have digit:192
Have digit:168
Have digit:4
Have digit:1
我们先来一个例子:
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:" <<age.c_str() << std::endl;
start = what[0].second;
}
我们希望得到的是打印人名,然后打印年龄。但是效果令我们大失所望:
Name:My age is 28 His
Age:27
嗯,查找原因:这是由于"+"号或者"*"号等重复符号带来的副作用,这些符号会消耗尽可能多的输入,使之是“贪婪”的。即正则表达式(.*)会匹配最长的串,而不是匹配最短的成功串。
如何使得这些重复的符号不再“贪婪”,我们在重复符号后加上"?"即可。
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:" <<age.c_str() << std::endl;
start = what[0].second;
}
Name:My
Age:28
Name: His
Age:27
regex_replace 学习
写了个去除左侧无效字符(空格,回车,TAB)的正则表达式。
std::string TrimLeft = "([//s//r//n//t]*)(//w*.*)";
boost::regex expression(TrimLeft);
testString = boost::regex_replace( testString, expression, "$2" );
std::cout<< "TrimLeft:" << testString <<std::endl;
TrimLeft:Hello World ! GoodBye World
typedef match_results<const char*> cmatch;
typedef match_results<std::string::const_iterator> smatch;
typedef match_results<const wchar_t*> wcmatch;
typedef match_results<std::wstring::const_iterator> wsmatch;
class match_results
{
#ifndef BOOST_NO_STD_ALLOCATOR
typedef std::vector<sub_match<BidiIterator>, Allocator> vector_type;
#else
typedef std::vector<sub_match<BidiIterator> > vector_type;
#endif
.....
private:
vector_type m_subs; // subexpressions
BidiIterator m_base; // where the search started from
sub_match<BidiIterator> m_null; // a null match
}
BidiIterator作为特化类型,const char* 特化为cmatch,std::string::const_iterator 特化为smatch。
typedef typename vector_type::const_iterator const_iterator;
typedef const_iterator iterator;
供我们来间接操作vector<sub_match<>>。
同时,也增加了如 prefix(),suffix()。
template <class BidiIterator>
struct sub_match : public std::pair<BidiIterator, BidiIterator>
{
typedef BidiIterator iterator;
typedef BidiIterator const_iterator;
}
BidiIterator接受const char *或std::string::const_iterator特化。
如const char *时,first分别指向串的起始指针,而std::string::const_iterator时,指向string::begin()和string::end()迭代器。
sub_match提供一个方法str(),它总是返回string类型。
#include "stdafx.h"
#include <string>
#include <boost/regex.hpp>
using namespace boost;
{
regex re("hello");
printf("re.mark_count = %d /n", re.mark_count()); //打印子表达式的个数,这里输出1
cmatch what;
{
//进行多种方式进行打印
//(*iter)和what[0]为sub_match值,调用其str()方法返回string类型
cmatch::iterator iter = what.begin();
cout << (*iter).str().c_str() << endl;
cout << what[0].str().c_str() << endl;
cout << what[0] << endl; // 对 << 进行了重载,间接调用了str();
//suffix()指向每个匹配串的尾部
cout << what.suffix() << endl;
sss = what.suffix();
}
}
它们分别按"("的位置进行排序,表达式本身为0号索引。
int main()
{
string sss = "hello hello";
regex re2(".*(he).*(he).*");
smatch result2;
if (regex_match(sss, result2, re2))
{
for (int i = 0; i < result2.size(); i ++)
cout << result2[i] << endl;
}
else
{
cout << "no match" << endl;
}
return 0;
}
输出结果:
hello hello
he
he
int main()
{
string sss = "he1llo he2llo";
regex re2 = string(".*(he//d).*");
smatch result2;
if (regex_match(sss, result2, re2))
{
for (int i = 0; i < result2.size(); i ++)
cout << result2[i] << endl;
}
else
{
cout << "no match" << endl;
}
return 0;
}
输出结果:
he2
regex re2 = string(".*?(he//d).*");
输出结果:
he1
内部机制是search所有的,并一一替换
regex re3("([//s//t//r//n]*)(//w*.*)");
string str3 = " hello ";
str3 = regex_replace(str3, re3, "$2");
regex re4("l");
str3 = regex_replace(str3, re4, "");
cout << str3 << endl;
关于正则表达式的Flag定义:
namespace regex_constants{
enum flag_type_
{
icase = ::boost::regbase::icase,
}
typedef ::boost::regbase::flag_type syntax_option_type;
}
typedef regex_constants::syntax_option_type flag_type;
boost::regbase::icase
regex_constants::icase
boost::regex::icase 这个定义没有找到
string ttt = re3.str();
printf("%s", ttt.c_str());
regex re5(ttt, regex::icase);