- #include <cstdlib>
- #include <stdlib.h>
- #include <boost/regex.hpp>
- #include <string>
- #include <iostream>
- 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<what.size();i++)
- cout<<"str :"<<what.str()<<endl;
- }
- else
- {
- cout<<"Error Input"<<endl;
- }
- return 0;
- }
regex对象与regex_match()函数
下面是一个检测两个字符串是否符合指定表达式的例子:
- #include <iostream>
- #include <cassert>
- #include <string>
- #include <boost/regex.hpp>
- using namespace std;
- using namespace boost;
- int main()
- {
- regex reg("//d{3}([a-zA-Z]+).(//d{2}|N/A)//s//1");
- string correct = "123Hello N/A Hello";
- string incorrect = "123Hello 12 hello";
- assert(regex_match(correct,reg)==true);
- assert(regex_match(incorrect,reg)==false);
- return 0;
- }
reg的格式含义是:三个数字,1个单词,一个任意字符,2个数字或者字符串N/A,1个空格,然后再重复第一个单词。
下面是一个匹配邮件地址的表达式例子:
- #include <iostream>
- #include <cassert>
- #include <string>
- #include <boost/regex.hpp>
- using namespace std;
- using namespace boost;
- int main()
- {
- regex reg("//w(.+)@(.+)//.com");
- string correct = "boluo1982107@ccc122.com";
- assert(regex_match(correct,reg)==true);
- return 0;
- }
很有意思吧!
regex_search()函数的使用
- #include <iostream>
#include <cassert>
#include <string>
#include <boost/regex.hpp>
using namespace std;
using namespace boost;
int main()
{
regex reg("(new)|(delete)");
smatch m;
string s = "Calls to new mustbe followed by delete. Calling simply new results in aleak!";
int new_counter = 0;
int delete_counter = 0;
string::const_iterator it = s.begin();
// string::const_iterator end = s.end();
while (regex_search(it,(string::const_iterator)s.end(),m,reg))
{
m[1].matched ? ++new_counter : ++delete_counter;
it = m[0].second;
}
if (new_counter != delete_counter)
cout<<"Leak detected!/n";
else
cout<<"Seems OK.../n";
return 0;
}
在上面的例子中,统计new和delete这两个单词的数目是不是一样。注意:smatch对象,它其实是一个match_results类型,分别用来记录reg中的索引是否被匹配。如果“new”被匹配了,那么m[1].matched就会为真(true);m[0].second表示it向后移动了,继续匹配剩下的字符串。
m[0],返回的是对匹配整个正则表达式的子匹配的引用,因此可以确定这个匹配的结束点通常是下次运行regex_search函数的起始点。
regex_replace()函数的使用
顾名思义,它可以用于执行文本替换。它在输入数据中进行搜索,查找正则表达式的所有匹配,对于每个匹配,该算法调用match_results::format,并将结果输出到一个传递给该函数的输出迭代器(output iterator)中。
下面的例子将英式拼法的colour替换为美式拼法的color。如果不使用正则表达式来进行这个拼写的修改,将会非常单调乏味,而且容易出错。问题在于单词中可能存在不同的大小写,而且单词可能还有很多的变形——例如colourize。为了正确地解决这个问题,需要把正则表达式分为3个子表达式:
- regex reg("(Colo)(u)(r)",boost::regex::icase|boost::regex::perl);
为了在任何匹配中能够很容易地删除字母u,我们对它进行了隔离。后面的是regex构造函数的标志参数,表示这个正则表达式不区分大小写;设置格式标志过程中的一个常见错误是忽略了regex需要默认启用的那些标志,如果没有设置这些标志,那么它们就不会启用,因此通常情况下必须使用应用应该设置的所有标志。
在使用regex_replace替换时,我们需要以参数的方式提供一个格式化字符串,该字符串决定如何进行替换。如果我们希望保留第一个和第三个匹配的子表达式,那么可以使用$N(N为子表达式的索引)来实现。下面是解决问题的完整代码:
- #include <iostream>
- #include <cassert>
- #include <string>
- #include <boost/regex.hpp>
- using namespace std;
- using namespace boost;
- int main()
- {
- regex reg("(Colo)(u)(r)",regex::icase | regex::perl);
- string s = "Colour, colours,color,colourize";
- s = regex_replace(s,reg,"$1$3");
- // s = regex_replace(s,reg,"$1 wocao $3");//尝试在输出字符串中加空格 cout<<s<<endl;
- return 0;
- }
关于重复和贪婪
- #include <iostream>
- #include <cassert>
- #include <string>
- #include <boost/regex.hpp>
- using namespace std;
- using namespace boost;
- int main()
- {
- regex reg("(.*)(//d{2})");
- cmatch m;
- const char * text = "Note that I'm 31 years old, not 32.";
- if (regex_search(text,m,reg))
- {
- if (m[1].matched)
- {
- cout<<m[1].str()<<'/n';
- }
- if (m[2].matched)
- {
- cout<<m[2]<<'/n';
- }
- }
- }
上面代码的输出结果是:
Note that I'm 31, not
32
也就是说,正则表达式中的".*"选项贪婪地吞掉了所有的输入!从而致使后面的子表达式无法获得匹配。按照我们的想法:31应该是符合/d{2}的条件的,应该被输出!但是可惜,它被*吞掉了。在正则表达式中,+和*都是重复性贪婪的。
为了非贪婪的重复,怎么办呢?可以在重复记号后面加一个问号"?",重复就会变成非贪婪的。修改上面代码的表达式:
- regex reg("(.*?)(//d{2})");
再运行程序,结果就变成:
Note that I'm
31
明白了吧?
regex_iterator的介绍和使用
- #include <iostream>
- #include <cassert>
- #include <string>
- #include <boost/regex.hpp>
- using namespace std;
- using namespace boost;
- class regex_callback
- {
- int sum_;
- public:
- regex_callback():sum_(0){}
- template<typename T> void operator()(const T& what)
- {
- sum_+=atoi(what[1].str().c_str());
- }
- int sum() const
- {
- return sum_;
- }
- };
- int main()
- {
- regex reg("(//d+),?");
- string s = "1,1,2,3,5,8,13,21";
- sregex_iterator it(s.begin(),s.end(),reg);
- sregex_iterator end;
- regex_callback c;
- int sum = for_each(it,end,c).sum();
- }
sregex_iterator是regex_iterator<std::string::const_iterator>的typedef,现在的使用方法更加清晰了。我们可以对比一下前面使用regex_search的时候,不得不在循环中手动地让起始迭代器前进,而且还要手动调用regex_search函数。
regex_token_iterator的介绍和使用
它和regex_iterator相似,但是它列举的是与正则表达式不匹配的字符序列,该特性对于字符串分割非常有用。当解引用regex_token_iterator时,只有被“预订”的子表达式才可以返回。下面这个例子:输入数据的条目之间用“/”分隔,要获得两个斜杠之间的数据;使用regex_token_iterator来分隔处理就特别简单,因为它的表达式简单:regex reg("/");为了使用这个正则表达式来分割输入,需要将一个特殊的索引-1传递给regex_token_iteraotr的构造函数。
- #include <iostream>
- #include <cassert>
- #include <boost/regex.hpp>
- using namespace std;
- using namespace boost;
- int main()
- {
- regex reg("/");
- string s = "Split/Values/Separated/By/Slashes,";
- vector<string> vec;
- sregex_token_iterator it(s.begin(),s.end(),reg,-1);
- sregex_token_iterator end;
- while (it!=end)
- vec.push_back(*it++);
- assert(vec.size()==count(s.begin(),s.end(),'/')+1);
- assert(vec[0]=="Split");
- return 0;
- }
regex_token_iterator是一个模板类,sregex_token_iterator是迭代器类型,是regex_token_iterator<std::string::const_iterator>的typedef。在上面的程序中,每次解引用返回的都是当前的sub_match,当迭代器前进时,它会尝试再次匹配该正则表达式。
换一个不同类型的输入,也应该能够写出程序来!
- #include <string>
- #include <iostream>
- #include <cassert>
- #include <boost/regex.hpp>
- using namespace std;
- using namespace boost;
- int main()
- {
- regex reg(" ");
- char* s="123 456 789 12345";
- // cregex_token_iterator it; //This is correct, too.
- regex_token_iterator<char*> it(s,s+strlen(s),reg,-1);
- regex_token_iterator<char*> end;
- while (it!=end)
- {
- cout<<*it++<<endl;
- }
- return 0;
- }
当需要反复调用regex_search时,考虑使用这两个iterator,比较方便。
/A和/Z的使用
如果把/A放在正则表达式的开始,把/Z放在正则表达式的最后,那么regex_search函数的行为就可以与regex_match函数的相同——也就是说,regex_search函数必须匹配所有的输入后才能匹配成功。
下面的表达式通常要求所有的输入都能获得匹配,而不管使用regex_match还是regex_search:
- regex reg("//A//d*//Z");
对比regex_search和regex_match的使用:
- regex reg("//d*");
- bool b=regex_match("17 is prime",reg);
- //返回失败
- regex reg("//d*");
- bool b=regex_search("17 is prime",reg);
- //返回true
- regex reg("//d*");
- bool b=regex_search("17 is prime",reg);
- //仍然返回失败
元字符(^)的使用
元字符用来表示取反。regex reg("[^13579]");它表示一个取反的字符类,可以匹配任意非奇数的字符。
- #include <iostream>
- #include <cassert>
- #include <boost/regex.hpp>
- using namespace std;
- using namespace boost;
- int main()
- {
- regex reg("[^13579]");
- string s="0123456789";
- sregex_iterator it(s.begin(),s.end(),reg);
- sregex_iterator end;
- while (it!=end)
- cout<<*it++;
- return 0;
- }
上面程序的输出结果为“02468”,如果输入字符串是“abcd”,也会全部匹配(因为它们不是奇数)。
另外,元字符^还可以用来表示一行的开始,元字符$则表示一行的结束。
无效正则表达式与异常抛出
下面这个程序还可以当作正则表达式格式输入的练习:)
- #include <string>
- #include <iostream>
- #include <cassert>
- #include <boost/regex.hpp>
- using namespace std;
- using namespace boost;
- int main()
- {
- cout<<"Enter a regular expression:/n";
- string s;
- getline(cin,s);
- try
- {
- regex reg(s);
- cout<<"Now enter a string to be matched:/n";
- getline(cin,s);
- if (regex_match(s,reg))
- cout<<"That's right!/n";
- else
- cout<<"No, that doesn't match!";
- }
- catch (bad_expression & e)
- {
- cout<<"That's not a valid expression! Error: "<<e.what()<<endl;
- }
- return 0;
- }
输入/d{5},输入:12345,输出:That's right!(奇怪,为什么这里输入/d而不是//d呢?输入//d没有抛出异常,却不能正确匹配!如果我输入//d,而这又是一个合法的正则表达式,那么它的匹配字符串是怎样的?)
输入错误的正则表达式:(/w*)),程序会抛出异常。
参考文献:超越c++标准库 Boost