当前位置: 首页 > 工具软件 > RE2 > 使用案例 >

C/C++中正则表达式库RE2的使用

诸葛嘉熙
2023-12-01

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,用于改变默认的设置,它是一个枚举类型,枚举值为:

  • DefaultOptions ,默认
  • Latin1, //将输入当做Latin-1(ISO-8859-1)编码,默认为UTF-8
  • POSIX, // POSIX语法,即从左开始最长匹配
  • 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的参考文档。

 类似资料: