C++ 中正则表达式(regex)库已经很多。光 boost 中就有3个:regex、spirit、xpressive.那么我们为什么还需要一个新的呢?

多数正则表达式库都需要一个编译(compile)过程。即:通过解释一个正则表达式的字符串(pattern)来生成该正则表达式的内部表示(字节码)。例如 boost regex 就是这样。这类我们称之为动态正则表达式库。

spirit、xpressive 例外。他们直接通过重载 C++ 的操作符来表达一个正则表达式。在你用C++语法描述完一个正则表达式,它已经是内部表示(被C++编译器编译成了机器码)。这一类我们称之为静态正则表达式库。

静态正则表达式库的好处主要有二:

性能好。由于匹配代码直接编译成为了机器码,故此通常性能会好过动态的正则表达式。

与 C++ 语言可形成良好的互动。可以非常容易在正则表达式中获得执行C++代码的时机。

缺点:

正则表达式必须在编译期确定。如果你希望用户可以输入一个正则表达式,那么静态正则表达式库不能直接满足你的需求。

TPL 属于静态正则表达式库。本文也不准备讨论动态正则表达式。需要指出,xpressive 既支持动态正则表达式,也支持静态的正则表达式,但是我们并不考虑其动态正则表达式部分。

TPL 全称为 Text Processing Library(文本处理库)。spirit、xpressive 是很好的东西,实现 TPL 库中对这两者有所借鉴。

说起来开发 TPL 库的理由看起来挺好笑的:原因是 spirit、xpressive 太慢。不是执行慢,而是编译慢。我的机器算起来也不算差,但是每次修改一点点代码,编译过程都等待半天,实在受不了这样的开发效率。

从机理上讲,TPL 并无特别让人振奋之处。该有的 spirit、xpressive 相信都有了。三者都基于“表达式模板(Expression Templates)” 这样的技术。

闲话少说,这里给几个实际的样例让大家感受下:

样例一:识别以空格分隔的浮点数并放入vector中代码:

#include <vector>
#include <tpl/RegExp.h>
using namespace tpl;

// What we use:
//    * Rules: /assign(), %, real(), ws()
//    * Matching: tpl::simple::match()

void simplest()
{
   std::vector<double>values;// you can change vector to other stl containers.
   if ( simple::match(
       "-.1 -0.1 +32. -22323.2e+12",
        real()/assign(values) %ws()) )
   {
       for (
           std::vector<double>::iterator it =values.begin();
           it !=values.end(); ++it)
       {
           std::cout << *it <<"\n";
       }   }}

输出:

-0.1

-0.1

-32

-2.23232e+016

解释:

以上代码我相信比较难以理解的是 / 和 % 算符。

/ 符号我称之为“约束”或“动作”。它是在一个规则(Rule)匹配成功后执行的额外操作。这个额外的操作可能是:

使用另一个Rule进行进一步的数据合法性检查。

赋值(本例就是)。

打印调试信息(正则表达式匹配比较难以跟踪,故此 Debug 能力也是 TPL 的一个关注点)。

其他用户自定义动作。

% 符号是列表算符(非常有用)。A % B 等价于 A (B A)* 这样的正则表达式。可匹配 ABABAB……A 这样的串。一个典型案例是用它匹配函数参数列表。

样例二:识别以逗号分隔的浮点数并放入vector中代码:

// A simple grammar example.
// What we use:
//    * Rules: /assign(), %, real(), gr(','), skipws()
//    * Matching: tpl::simple::match()

void simple_grammar()
{
   std::vector<double>values;// you can change vector to other stl containers.

if ( simple::match(
       " -.1 , -0.1 , +32. , -22323.2e+12",
       real()/assign(values) %gr(','),skipws()) )
   {
       for (
           std::vector<double>::iterator it =values.begin();
           it !=values.end(); ++it)
       {
           std::cout << *it <<"\n";
       }
   }
}

输出:与样例一相同。

解释:尽管看起来好像没有发生太大的变化。但是这两个样例本质上是不同的。主要体现在:

正则表达式的类型不同。real()/assign(values) % ws() 是一个Rule.而 real()/assign(values) % gr(',') 是一个 Grammar.简单来说,Rule 可以认为是词法级别的东西。Grammar 是语法级别的东西。Grammar 的特点在于,它匹配一个语法单元前,总会先调用一个名为Skipper的特殊Rule.上例中 Skipper 为 skipws()。

两个 match 的原型不同。第一个match的原型是:match(Source, Rule), 第二个match的原型是:match(Source, Grammar, Skipper)。

第二个例子如果用 Rule 而不是用 Grammar 写,看起来是这样的:

if ( simple::match(    " -.1 , -0.1 , +32. , -22323.2e+12 ",    (skipws() + real()/assign(values)) % (skipws() + ',')) ) ...

你可能认为这并不复杂。单对这个例子而言,确实看起来如此。但是如果你这样想,不妨用 Rule 做下下面这个例子。

样例三:运算器(Calculator)

功能:可处理+-*/四则运算、()、函数调用(sin, cos, pow)。代码:(呵呵,只有60行代码哦!)

#include <stack>
#include <tpl/RegExp.h>
#include <tpl/ext/Calculator.h>
#include <cmath>
using namespace tpl; void calculate2()
{   
    typedef SimpleImplementation impl; 
    // ---- define rules ----
     impl::Allocator alloc; 
    std::stack<double> stk;
     impl::Grammar::Var rFactor; 
    impl::Grammar rMul( alloc, '*' + rFactor/calc<std::multiplies>(stk) );
    impl::Grammar rDiv( alloc, '/' + rFactor/calc<std::divides>(stk) );
    impl::Grammar rTerm( alloc, rFactor + *(rMul | rDiv) ); 
    impl::Grammar rAdd( alloc, '+' + rTerm/calc<std::plus>(stk) ); 
   impl::Grammar rSub( alloc, '-' + rTerm/calc<std::minus>(stk) );
    impl::Grammar rExpr( alloc, rTerm + *(rAdd | rSub) );
     impl::Rule rFun( alloc,         "sin"/calc
    (stk, sin) | "cos"/calc(stk, cos) | "pow"/calc(stk, pow) );  
   rFactor.assign( alloc,
         real()/assign(stk) | 
       '-' + rFactor/calc<std::negate>(stk) | 
       '(' + rExpr + ')' | 
       (gr(c_symbol()) + '(' + rExpr % ',' + ')')/(gr(rFun) + '(') | 
       '+' + rFactor ); 
    // ---- do match ----
     for (;;) 
   {
        std::string strExp; 
       std::cout << "input an expression (q to quit): "; 
       if (!std::getline(std::cin, strExp) || strExp == "q") { 
           std::cout << '\n'; 
           break;
        }
         try { 
           while ( !stk.empty() )
           stk.pop(); 
           if ( !impl::match(strExp.c_str(), rExpr + eos(), skipws()) )  
              std::cout << ">>> ERROR: invalid expression!\n";  
          else
                std::cout << stk.top() << "\n"; 
       }
        catch (const std::logic_error& e) { 
           std::cout << ">>> ERROR: " << e.what() << "\n"; 
       } 
   }
}
// -------------------------------------------------------------------------

解释:

Grammar::Var 用于定义一个未赋值即被引用的Grammar.相应地,我们也有 Rule::Var.

gr(Rule) 是将一个 Rule 转换为 Grammar.

SimpleImplementation 是什么?嗯,这个下回聊。

<tpl/ext/Calculator.h> 并不属于 tpl regex 库。代码也不多

转载于:https://www.cnblogs.com/wanghao111/archive/2009/09/21/1571370.html

C++中regex库静态正则表达式库的好处及事例相关推荐

  1. Linux下Makefile中动态链接库和静态链接库的生成与调用

    背景:写这篇博客的原因是:最近在搞嵌入式,需要交叉编译opencv库文件,自己写Makefile,通过arm-linux-g++编译.链接.生成可执行文件,从而实现了移植的过程.平台是Toradex的 ...

  2. 深入浅出C/C++中的正则表达式库(一)--GNU Regex Library

    正则表达式(Regular Expressions),又被称为regex或regexp,是一种十分简便.灵活的文本处理工具.它可以用来精确地找出某文本中匹配某种指定规则的内 容.在linux下,gre ...

  3. linux 中如何将文件粘贴到usr下的lib内,学会在Linux下GCC生成和使用静态库和动态库...

    一.基本概念1.1什么是库 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的平台不同(主 ...

  4. linux系统中 库分为静态库和,你知道linux 静态库和共享库?

    1.静态库和共享库 静态库和共享库(动态库),二者的不同点在于代码被载入的时刻不同. 静态库的代码在编译过程中已经被载入可执行程序,因此体积较大. 共享库的代码是在可执行程序运行时才载入内存的,在编译 ...

  5. vc 可用的正则表达式库

    vc 可用的正则表达式库 gnuregex,PCRE,greta,boost,CAtlReg 其中 CAtlReg 和greta 是微软的,不过 greta 据说已经多年不维护,不能在最新的编译器中编 ...

  6. MFC模块的动态链接库DLL以及静态链接库LIB编译后的调用

    静态链接库LIB和动态链接库DLL的区别,创建和示例   1.什么是静态连接库,什么是动态链接库   静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都 ...

  7. Linux下Gcc生成和使用静态库和动态库详解

    参考文章:http://blog.chinaunix.net/uid-23592843-id-223539.html 一.基本概念 1.1什么是库 在windows平台和linux平台下都大量存在着库 ...

  8. Linux下GCC生成和使用静态库和动态库详解(二)

    2.1准备好测试代码hello.h.hello.c和main.c: hello.h(见程序1)为该函数库的头文件. hello.c(见程序2)是函数库的源程序,其中包含公用函数hello,该函数将在屏 ...

  9. Linux下Gcc生成和使用静态库和动态库详解(转)

    一.基本概念 1.1什么是库 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的平台不同( ...

  10. GCC 编译 C(C++)静态链接库(gcc -L、gcc -l)和动态链接库(gcc -fPIC -shared)的创建和使用

    1. 库文件 所谓库文件,读者可以将其等价为压缩包文件,该文件内部通常包含不止一个目标文件(也就是二进制文件). 值得一提的是,库文件中每个目标文件存储的代码,并非完整的程序,而是一个个实用的功能模块 ...

最新文章

  1. python画图程序飞机_Python海龟画图工具绘制叮当猫程序
  2. 不同坐标系下角速度_技术 | 西安80坐标与地方坐标系的转换方法技巧
  3. 机器学习算法之——隐马尔可夫模型(Hidden Markov Models,HMM) 代码实现
  4. 转 Django+Bootstrap练习--我的类博客系统开发
  5. 第十章:Java_IO流
  6. 看完这篇你就明白,为什么说大部分企业建设数据中台都会失败?
  7. hypermesh10的安装
  8. 数据--第34课 - 二叉树的深层性质
  9. 计算机ppt基础知识题库,计算机二级考试MSOffice考试题库ppt操作题附答案.pdf
  10. 图易服装PDM产品数据管理系统
  11. 20个你需要知道的JavaScript简写代码片段
  12. 计算机专业研究生的读研规划思考------转载
  13. Android 实现扫一扫功能
  14. openAL在C++下的易用封装,调用直接播放3D音频,模拟3D音效
  15. 运维工程师被墨菲定律的各种打脸之DXX问题
  16. Could not get a resource from the pool 问题解决
  17. MNN实践[C++版本]
  18. Linux 机器重启reboot命令
  19. 网上书店订单流程c语言源代码,网上书店的设计及实现.doc
  20. 25-30K ☀️|网络工程师职业技巧与经典面试题✨

热门文章

  1. DOM编程系列之Node对象个人分享
  2. IAR_STM32_BootLoader
  3. 数据结构C++ 栈——栈的应用
  4. unity探索者之微信分享回调
  5. Android_撕衣服小案例
  6. [Node.js]Domain模块
  7. ARM 中断状态和SVC状态的堆栈切换 (异常)【转】
  8. 卡耐基口才艺术与人际关系1
  9. 一些碰到的陌生的技术名词搜集(持续更新……)
  10. ubuntu ffmpeg 下载安装