RE2是Google开源的正则表达式库,RE2是多语言支持的,提供对C/C++,Python,Ruby等,本文介绍C语言的RE2的正则库。正则表达式在工程实践中是非常有用的,可用于信息提取,内容比对,替换等。
安装
主要步骤,下载安装包[GitHub地址],解压,编译,安装。需要说明的是较新版本源码添加了对C++11的支持,同时也需要较高版本g++的支持,本文选择tag为re2-2016-02-01的较低版本。
类简介
使用RE2首先要构造正则表达式对象,RE2的构造函数如下:
RE2(const StringPiece& pattern);
RE2(const StringPiece& pattern, const Options& option);
其中StringPiece是RE2提供的一个string风格的对象,在RE2构造函数中传入”const char*” 或者”string”对象时,将被隐式的转换为StringPiece对象。使用StringPiece可以减少”const char*”到”string”之间的来来回回的转换。StringPiece包含一个指向原始输入的指针,和一个字符串的长度计数,构造函数如下:
class StringPiece {
private:
const char* ptr_;
int length_;
public:
StringPiece() : ptr_(NULL), length_(0) { }
StringPiece(const char* str): ptr_(str), length_((str == NULL) ? 0 : static_cast<int>(strlen(str))) { }
StringPiece(const std::string& str): ptr_(str.data()), length_(static_cast<int>(str.size())) { }
StringPiece(const char* offset, int len) : ptr_(offset), length_(len) { }
...
};
Options:构造函数中的Options,用于改变默认的设置,它是一个枚举类型,枚举值为:
Quiet // 不要输出正则表达式编译的错误等
RE2类的其他方法:
//返回RE2对象是否被正确的创建
bool ok() const { return error_code() == NoError; }
//如果RE2对象没有被正确的创建,将返回相关的错误描述
const string& error() const { return *error_; }
使用
下面介绍使用正则表达式提取结果的方法
1.Extract
/*
*用于从text内容中,根据正则表达式提取内容
*text:待提取的内容
*pattern:构造,编译后的正则表达式
*rewrite:控制输出结果
*out:按照rewrite定义的输出结构
*/
static bool Extract(const StringPiece &text, const RE2& pattern, const StringPiece &rewrite, string *out);
text:待提取的内容
pattern:构造,编译后的正则表达式
rewrite:控制输出结果
out:按照rewrite定义的输出结构
这里重点说下rewrite,它用于控制输出结果,如果正则表达式含有多个字表达式,那么rewrite可以控制输出哪些匹配结果,它通常是’[digits]’,其中\0表示完整的匹配结果,而\1则表示第一个字表达式。例如:表达式regmail中含有多个字表达式,下面分别用\1,\3,\5分别输出邮箱的用户名,用户名等。
string content = "test,regular,邮件 test@gmail.com;";
string regmail = "(\\w+([-+.]\\w+)*)@(\\w+([-.]\\w+)*)\\.(\\w+([-.]\\w+)*)";
re2::RE2::Extract(content.c_str(),*pHandle,"\\0,\\1,\\3,\\5",&strVal);
strVal的值为"test@gmail.com,test,gmail,com" //分隔符','是在rewrite中定义的符号
2.Match
bool Match(const StringPiece& text,int startpos, int endpos, Anchor anchor, StringPiece *match, int nmatch) const;
text:待匹配的文本
startpos:从text中进行匹配的开始位置
endpos:从text中进行匹配的结束位置
anchor:匹配的模式,Anchor是一个枚举类型,其值分别为:UNANCHORED表示从startpos到endpos之间任何位置开始匹配,ANCHOR_START表示只能从startpos开始匹配, ANCHOR_BOTH表示模式必须完全匹配startpos到endpos之间的字符串。
match:它通常是是一个StringPiece数组,用于保存匹配结果
nmatch:匹配的字段个数,通常nmatch值越小,匹配速度越快,应该避免使得nmatch>1+match数组的长度
下面介绍Anchor类型对匹配结果的影响:
StringPiece group[3];
RE2 re("(\\w+):([0-9]+)");
string content = "please visit 127.0.0.1:8999 here";
if(re.Match(content,0,content.size(),RE2::UNANCHORED,group,3))
{
for(size_t i = 0; i < 3 ;++i)
{
cout<<group[i]<<";";
}
cout<<endl;
}
上述的正则表达式包含两个子表达式,group[0]是完整的匹配结果,group[1],group[2]分别输出模式”(\w+)”和”([0-9]+)”的结果,其输出如下:localhost:8999;localhost;8999;
如果将匹配模式改为:
re.Match(content,0,content.size(),RE2::ANCHOR_START,group,3);
那么只能匹配content值为”127.0.0.1:8999 here”,ANCHOR_START指明模式必须从startpos就开始匹配。
而如果将匹配模式改为:
re.Match(content,0,content.size(),RE2::ANCHOR_BOTH,group,3);
那么只能匹配content值为”127.0.0.1:8999”,ANCHOR_BOTH指明模式必须从startpos到endpos之间完全匹配。
3.FullMatch
static bool FullMatchN(const StringPiece& text, const RE2& re,const Arg* const args[], int argc);
static const VariadicFunction2<bool, const StringPiece&, const RE2&, Arg, RE2::FullMatchN> FullMatch;
FullMatch通过模板VariadicFunction2,支持可变参数,将要提取的表达式,通过FullMatchN函数实现。使用:
string content = "please visit localhost:8999 here";
string host;
int port;
RE2::FullMatch("local:8999","(\\w+):([0-9]+)",&host,&port);
匹配模式中含有两个子表达式,其值分别通过变量host和port进行提取。使用RE2::FullMatch(“local:8999”,”(\w+):([0-9]+)”,&host,&port);形式调用FullMatch,每次调用的时编译一次正则表达式,如果多次使用同一个匹配模式,这样会造成很多不必要的开销。可以编译一次正则表达式,保存到一个RE2对象中,然后在每次调用时重用这个对象。如:
RE2 re("(\\w+):([0-9]+)");
RE2::FullMatch("master:8088", re, &host, &port);
4.Replace & GlobalReplace
static bool Replace(string *str, const RE2& pattern, const StringPiece& rewrite);
static int GlobalReplace(string *str, const RE2& pattern, const StringPiece& rewrite);
str:待替换的原始字符串,注意这一个指针,所有如果有匹配结果,将会直接修改原始数据;
pattern:匹配的模式,即正则表达式
rewrite:替换的模式,可以带有'\[digits]'表示的子模式结果
两者都可以用于替换,区别在于前只替换第一个匹配的结果,后者则会匹配所有匹配结果。关于返回值:前者返回是否替换成功(最多只会替换一次),后者返回全局整体被成功替换的次数。如下:
RE2 re("(\\w+):([0-9]+)");
string content = "please visit localhost:8999 master:8088 here";
if(RE2::Replace(&content, re, "127.0.0.1:\\2"))
{
cout<<content<<endl; //"please visit 127.0.0.1:8999 master:8088 here" 只有第一个匹配结果被替换
}
content = "please visit localhost:8999 or master:8088 here";
cout<<"relapce count:"<<RE2::GlobalReplace(&content, re, "127.0.0.1:\\2")<<endl; //"2",即有两处被替换
out<<content<<endl; //"please visit 127.0.0.1:8999 or 127.0.0.1:8088 here"所有匹配结果都被替换
Demo
// g++ -g testRE2.cpp -o run -lre2
#include <iostream>
#include <string>
#include "re2/re2.h"
using namespace std;
using namespace re2;
int main()
{
{
string regmail = "(\\w+([-+.]\\w+)*)@(\\w+([-.]\\w+)*)\\.(\\w+([-.]\\w+)*)";
RE2 *pHandle = NULL;
pHandle = new RE2(regmail, re2::RE2::Quiet);
if( !pHandle->ok() )
{
cout<<pHandle->error()<<endl;
return -1;
}
string content = "test,regular,邮件 test@gmail.com; ";
string strVal ;
if(re2::RE2::Extract(content.c_str(),*pHandle,"\\0,\\1,\\3,\\5",&strVal))
{
cout<<strVal<<endl;
}
if(pHandle != NULL)
{
delete pHandle;
pHandle = NULL;
}
}
{
StringPiece group[3];
RE2 re("(\\w+):([0-9]+)");
string content = "please visit localhost:8999 here";
if(re.Match(content,0,content.size(),RE2::UNANCHORED,group,3))
{
for(size_t i = 0; i < 3 ;++i)
{
cout<<group[i]<<";";
}
cout<<endl;
}
}
{
string host;
int port;
string content = "please visit localhost:8999 here";
RE2::FullMatch("localhost:8999","(\\w+):([0-9]+)",&host,&port);
cout<<host<<":"<<port<<endl;
RE2 re("(\\w+):([0-9]+)");
RE2::FullMatch("master:8088", re, &host, &port);
cout<<host<<":"<<port<<endl;
}
{
RE2 re("(\\w+):([0-9]+)");
string content = "please visit localhost:8999 here";
if(RE2::Replace(&content, re, "127.0.0.1"))
{
cout<<content<<endl;
}
content = "please visit localhost:8999 or master:8088 here";
cout<<"relapce count:"<<GlobalReplace(&content, re, "127.0.0.1")<<endl;
cout<<content<<endl;
}
return 0;
}
本文简介了使用C/C++语言操作RE2库的主要方法,至于正则表达式的语法详见RE2的参考文档。