C++中三种正则表达式比较

工作需要用到C++中的正则表达式,所以就研究了以上三种正则。

1、C regex

/*  write by xingming  *  time:2012年10月19日15:51:53  *  for: test regex  *  */

#include <regex.h>
#include <iostream>
#include <sys/types.h>
#include <stdio.h>
#include <cstring>
#include <sys/time.h>

using namespace std;
const int times = 1000000;

int main(int argc,char** argv) {
    char pattern[512]="finance\.sina\.cn|stock1\.sina\.cn|3g\.sina\.com\.cn.*(channel=finance|_finance$|ch=stock|/stock/)|dp.sina.cn/.*ch=9&";
    const size_t nmatch = 10;
    regmatch_t pm[10];
    int z ;
    regex_t reg;
    char lbuf[256]="set",rbuf[256];
    char buf[3][256] = {"finance.sina.cn/google.com/baidu.com.google.sina.cndddddddddddddddddddddda.sdfasdfeoasdfnahsfonadsdf",
                    "3g.com.sina.cn.google.com.dddddddddddddddddddddddddddddddddddddddddddddddddddddbaidu.com.sina.egooooooooo",
                    "http://3g.sina.com.cn/google.baiduchannel=financegogo.sjdfaposif;lasdjf.asdofjas;dfjaiel.sdfaosidfj"};
    printf("input strings:\n");
    timeval end,start;
    gettimeofday(&start,NULL);
    regcomp(&reg,pattern,REG_EXTENDED|REG_NOSUB);
    for(int i = 0 ; i < times; ++i)
    {
        for(int j = 0 ; j < 3; ++j)
        {
            z = regexec(&reg,buf[j],nmatch,pm,REG_NOTBOL);
/*          if(z==REG_NOMATCH)                 printf("no match\n");             else                 printf("ok\n");                 */
        }
    }
    gettimeofday(&end,NULL);
    uint time = (end.tv_sec-start.tv_sec)*1000000 + end.tv_usec - start.tv_usec;
    cout<<time/1000000<<" s and "<<time%1000000<<" us."<<endl;
    return 0 ;
}

使用正则表达式可简单的分成几步:

  • 1.编译正则表达式
  • 2.执行匹配
  • 3.释放内存

首先,编译正则表达式

int regcomp(regex_t *preg, const char *regex, int cflags);

reqcomp()函数用于把正则表达式编译成某种格式,可以使后面的匹配更有效。

preg: regex_t结构体用于存放编译后的正则表达式;

regex: 指向正则表达式指针;

cflags:编译模式

共有如下四种编译模式:

REG_EXTENDED:使用功能更强大的扩展正则表达式

REG_ICASE:忽略大小写

REG_NOSUB:不用存储匹配后的结果

REG_NEWLINE:识别换行符,这样‘$’就可以从行尾开始匹配,‘^’就可以从行的开头开始匹配。否则忽略换行符,把整个文本串当做一个字符串处理。

其次,执行匹配

int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);

preg: 已编译的正则表达式指针;

string:目标字符串;

nmatch:pmatch数组的长度;

pmatch:结构体数组,存放匹配文本串的位置信息;

eflags:匹配模式

共两种匹配模式:

REG_NOTBOL:The match-beginning-of-line operator always fails to
match  (but see  the  compilation  flag  REG_NEWLINE above). This flag
may be used when different portions of a string are passed  to  regexec
and the beginning of the string should not be interpreted as the
beginning of the line.

REG_NOTEOL:The match-end-of-line operator always fails to  match  (but  see the compilation flag REG_NEWLINE above)

最后,释放内存

void regfree(regex_t *preg);

当使用完编译好的正则表达式后,或者需要重新编译其他正则表达式时,一定要使用这个函数清空该变量。

其他,处理错误

size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);

当执行regcomp 或者regexec 产生错误的时候,就可以调用这个函数而返回一个包含错误信息的字符串。

errcode: 由regcomp 和 regexec 函数返回的错误代号。

preg: 已经用regcomp函数编译好的正则表达式,这个值可以为NULL。

errbuf: 指向用来存放错误信息的字符串的内存空间。

errbuf_size: 指明buffer的长度,如果这个错误信息的长度大于这个值,则regerror 函数会自动截断超出的字符串,但他仍然会返回完整的字符串的长度。所以我们可以用如下的方法先得到错误字符串的长度。

当然我在测试的时候用到的也比较简单,所以就直接用了,速度一会再说!

2、C++ regex

/*  write by xingming  *  time:2012年10月19日15:51:53  *  for: test regex  *  */

#include <regex>
#include <iostream>
#include <stdio.h>
#include <string>

using namespace std;

int main(int argc,char** argv) {
    regex pattern("[[:digit:]]",regex_constants::extended);
    printf("input strings:\n");
    string buf;

    while(cin>>buf)
    {
        printf("*******\n%s\n********\n",buf.c_str());

        if(buf == "quit")
        {
            printf("quit just now!\n");
            break;
        }

        match_results<string::const_iterator> result;
        printf("run compare now!  '%s'\n", buf.c_str());
        bool valid = regex_match(buf,result,pattern);
        printf("compare over now!  '%s'\n", buf.c_str());

        if(!valid)
            printf("no match!\n");
        else
            printf("ok\n");
    }

    return 0 ;
}

C++这个真心不想多说它,测试过程中发现 字符匹配的时候 ‘a’
是可以匹配的,a+也是可以的,[[:w:]]也可以匹配任意字符,但[[:w:]]+就只能匹配一个字符,+号貌似不起作用了。所以后来就干脆放弃了这伟大的C++正则,如果有大牛知道这里面我错在哪里了,真心感谢你告诉我一下,谢谢。

3、boost regex

/* write by xingming
 * for:test boost regex
 * time:2012年10月23日11:35:33
 * */  #include <iostream> #include <string> #include <sys/time.h>
#include "boost/regex.hpp"

using namespace std;
using namespace boost;
const int times = 10000000;

int main()
{
    regex  pattern("finance\\.sina\\.cn|stock1\\.sina\\.cn|3g\\.sina\\.com\\.cn.*(channel=finance|_finance$|ch=stock|/stock/)|dp\\.s ina\\.cn/.*ch=9&");
    cout<<"input strings:"<<endl;
    timeval start,end;
    gettimeofday(&start,NULL);
    string input[] = {"finance.sina.cn/google.com/baidu.com.google.sina.cn",
                      "3g.com.sina.cn.google.com.baidu.com.sina.egooooooooo",
                      "http://3g.sina.com.cn/google.baiduchannel=financegogo"};
    for(int i = 0 ;i < times; ++ i)
    {
        for(int j = 0 ; j < 3;++j)
        {
            //if(input=="quit")
            //  break;
            //cout<<"string:'"<<input<<'\''<<endl;
            cmatch what;
            if(regex_search(input[j].c_str(),what,pattern)) ;
            //  cout<<"OK!"<<endl;
            else ;
            //  cout<<"error!"<<endl;
        }
    }
    gettimeofday(&end,NULL);
    uint time = (end.tv_sec-start.tv_sec)*1000000 + end.tv_usec - start.tv_usec;
    cout<<time/1000000<<" s and "<<time%1000000<<" us."<<endl;
    return 0 ;
}

boost正则不用多说了,要是出去问,C++正则怎么用啊?那90%的人会推荐你用boost正则,他实现起来方便,正则库也很强大,资料可以找到很多,所以我也不在阐述了。

4、对比情况

总结:

C regex的速度让我吃惊啊,相比boost的速度,C regex的速度几乎要快上3倍,看来正则引擎的选取上应该有着落了!

上面的表格中我用到的正则和字符串是一样的(在代码中C regex的被我加长了),速度相差几乎有3倍,C的速度大约在30+w/s , 而boost的速度基本在15-w/s ,所以对比就出来了!

在这里Cregex的速度很让我吃惊了已经,但随后我的测试更让我吃惊。

我以前在.net正则方面接触的比较多,就写了一个.net版本的作为对比

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace 平常测试
{
    class Program
    {
        static int times = 1000000;
        static void Main(string[] args)         {
            Regex reg = new Regex(@"(?>finance\.sina\.cn|stock1\.sina\.cn|3g\.sina\.com\.cn.*(?:channel=finance|_finance$|ch=stock|/stock/)|dp.sina.cn/.*ch=9&)",RegexOptions.Compiled);
            string[] str = new string[]{@"finance.sina.cn/google.com/baidu.com.google.sina.cn",
                    @"3g.com.sina.cn.google.com.baidu.com.sina.egooooooooo",
                    @"http://3g.sina.com.cn/google.baiduchannel=financegogo"};
            int tt = 0;
            DateTime start = DateTime.Now;
            for (int i = 0; i < times; ++i)
            {
                for (int j = 0; j < 3; ++j)
                {
                    if (reg.IsMatch(str[j])) ;
                        //Console.WriteLine("OK!");
                    //else
                        //Console.WriteLine("Error!");
                }
            }
            DateTime end = DateTime.Now;
            Console.WriteLine((end - start).TotalMilliseconds);
            Console.WriteLine(tt);
            Console.ReadKey();
        }
    }
}

结果发现,正则在不进行RegexOptions.Compiled 的时候,速度和C regex的基本一样,在编译只会,速度会比C regex快上一倍,这不由得让我对微软的那群人的敬畏之情油然而生啊。

但随后我去查看了一下该博客上面C regex的描述,发现我可以再申明正则的时候加入编译模式,随后我加入了上面代码里的
REG_NOSUB(在先前测试的时候是没有加入的),结果让我心理面很激动的速度出来了,C regex 匹配速度竟然达到了
300+w/s,也就是比原来的(不加入REG_NOSUB)的代码快了将近10倍。

之后我变换了匹配的字符串,将其长度生了一倍,达到每个100字符左右(代码里面所示),匹配速度就下来了,但是也能达到 100w/s左右,这肯定满足我们现在的需求了。

结果很显然,当然会选择C regex了。

作者:pmars的博客

来源:51CTO

时间: 2024-10-31 19:10:14

C++中三种正则表达式比较的相关文章

正则表达式在IOS中的应用及IOS中三种正则表达式的使用与比较_正则表达式

正则表达式在ios中应用 一.什么是正则表达式 正则表达式,又称正规表示法,是对字符串操作的一种逻辑公式.正则表达式可以检测给定的字符串是否符合我们定义的逻辑,也可以从字符串中获取我们想要的特定部分.它可以迅速地用极简单的方式达到字符串的复杂控制. 二.正则表达式的语法 看一个过滤纯数字的例子 - (BOOL)validateNumber:(NSString *) textString { NSString* number=@"^[0-9]+$"; NSPredicate *numbe

Android 中三种启用线程的方法总结

在多线程编程这块,我们经常要使用Handler(处理),Thread(线程)和Runnable这三个类,那么他们之间的关系你是否弄清楚了呢? 首先说明Android的CPU分配的最小单元是线程,Handler一般是在某个线程里创建的,因而Handler和Thread就是相互绑定的,一一对应. 而Runnable是一个接口,Thread是Runnable的子类.所以说,他俩都算一个进程. HandlerThread顾名思义就是可以处理消息循环的线程,他是一个拥有Looper的线程,可以处理消息循环

Oracle中三种表连接算法的总结

Oracle有三种表连接技术,分别是嵌套连接.合并连接和哈希连接.以下就是对这三种表连接算法进行了详细的分析介绍,需要的朋友可以参考下   1. 嵌套循环连接 (NESTED LOOP Join)嵌套连接把要处理的数据集分为外循环(驱动数据源)和内循环(被驱动数据源),外循环只执行一次(先执行),内循环执行的次数等于外循环执行的数据集个数. 这种连接的好处是内存使用非常少. 如果驱动数据源有限,且被驱动表在连接列上有相应的索引,则这种连接方式才是高效的. 在OLTP系统上常见到这种连接方式. 2

【SSH系列】深入浅出spring IOC中三种依赖注入方式

spring的核心思想是IOC和AOP,IOC-控制反转,是一个重要的面向对象编程的法则来消减计算机程序的耦合问题,控制反转一般分为两种类型,依赖注入和依赖查找,依赖什么?为什么需要依赖?注入什么?控制什么?依赖注入和控制反转是一样的概念吗?接触新的知识,小编的脑袋中全是大大的问号,不过没有关系,今天这篇博文,小编主要来简单的介绍一下在spring IOC中依赖注入的方法. 依赖注入和控制反转,目的是为了使类与类之间解耦合,提高系统的可扩展性和可维护性.我们可以从以下几个方面理解: a.参与者都

java中三种方式获得类的字节码文件对象

package get_class_method; public class ReflectDemo { /** * @param args */ public static void main(String[] args) { getClassObject1(); getClassObject2(); getClassObject3(); } public static void getClassObject1(){ Person p = new Person(); System.out.pr

java中三种主流数据库数据库(sqlserver,db2,oracle)的jdbc连接总结

原文http://www.cnblogs.com/PatrickChen/archive/2008/12/08/1350395.html 其实java开发中连接数据库的主要问题不是如何写代码,而是找驱动包.因为jdbc的驱动包太多,并且名目繁多,有的还需要安装客户端才能运行. 项目实践中,总结出三种主流数据库中,Java的驱动和连接字符串,均无需安装客户端: 一.             Sqlserver: 驱动包:sqljdbc.jar 实例: String _driverName = "c

分享下php5类中三种数据类型的区别_php技巧

public: 公有类型 在子类中可以通过self::var 来调用 public类型的方法或属性 可以通过parent::method 来调用父类中的方法 在实例中可以能过$obj->var 来调用 public类型的方法或属性 protected: 受保护类型在子类中可以通过self::var 来调用 protected类型的方法或属性 可以通过parent::method 来调用父类中的方法 在实例中不能通过$obj->var 来调用 protected类型的方法或属性 private:

MySQL中三种关联查询方式的简单比较_Mysql

看看下面三个关联查询的 SQL 语句有何区别?   SELECT * FROM film JOIN film_actor ON (film.film_id = film_actor.film_id) SELECT * FROM film JOIN film_actor USING (film_id) SELECT * FROM film, film_actor WHERE film.film_id = film_actor.film_id 最大的不同更多是语法糖,但有一些有意思的东西值得关注.

JavaScript中三种异步上传文件方式_javascript技巧

异步上传文件是为了更好的用户体验,是每个前端必须掌握的技能.这里我提出三点有关异步文件上传的方式. 使用第三方控件,如Flash,ActiveX等浏览器插件上传. 使用隐藏的iframe模拟异步上传. 使用XMLHttpRequest2来实现异步上传. 第一种使用浏览器插件上传,需要一定的底层编码功底,在这里我就不讲了,以免误人子弟,提出这点大家可以自行百度. 第二种使用隐藏的iframe模拟异步上传.为什么在这里说的是模拟呢?因为我们其实是将返回结果放在了一个隐藏的iframe中,所以才没有使