C++中regex库静态正则表达式库的好处及事例
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库静态正则表达式库的好处及事例相关推荐
- Linux下Makefile中动态链接库和静态链接库的生成与调用
背景:写这篇博客的原因是:最近在搞嵌入式,需要交叉编译opencv库文件,自己写Makefile,通过arm-linux-g++编译.链接.生成可执行文件,从而实现了移植的过程.平台是Toradex的 ...
- 深入浅出C/C++中的正则表达式库(一)--GNU Regex Library
正则表达式(Regular Expressions),又被称为regex或regexp,是一种十分简便.灵活的文本处理工具.它可以用来精确地找出某文本中匹配某种指定规则的内 容.在linux下,gre ...
- linux 中如何将文件粘贴到usr下的lib内,学会在Linux下GCC生成和使用静态库和动态库...
一.基本概念1.1什么是库 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的平台不同(主 ...
- linux系统中 库分为静态库和,你知道linux 静态库和共享库?
1.静态库和共享库 静态库和共享库(动态库),二者的不同点在于代码被载入的时刻不同. 静态库的代码在编译过程中已经被载入可执行程序,因此体积较大. 共享库的代码是在可执行程序运行时才载入内存的,在编译 ...
- vc 可用的正则表达式库
vc 可用的正则表达式库 gnuregex,PCRE,greta,boost,CAtlReg 其中 CAtlReg 和greta 是微软的,不过 greta 据说已经多年不维护,不能在最新的编译器中编 ...
- MFC模块的动态链接库DLL以及静态链接库LIB编译后的调用
静态链接库LIB和动态链接库DLL的区别,创建和示例 1.什么是静态连接库,什么是动态链接库 静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都 ...
- Linux下Gcc生成和使用静态库和动态库详解
参考文章:http://blog.chinaunix.net/uid-23592843-id-223539.html 一.基本概念 1.1什么是库 在windows平台和linux平台下都大量存在着库 ...
- Linux下GCC生成和使用静态库和动态库详解(二)
2.1准备好测试代码hello.h.hello.c和main.c: hello.h(见程序1)为该函数库的头文件. hello.c(见程序2)是函数库的源程序,其中包含公用函数hello,该函数将在屏 ...
- Linux下Gcc生成和使用静态库和动态库详解(转)
一.基本概念 1.1什么是库 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的平台不同( ...
- GCC 编译 C(C++)静态链接库(gcc -L、gcc -l)和动态链接库(gcc -fPIC -shared)的创建和使用
1. 库文件 所谓库文件,读者可以将其等价为压缩包文件,该文件内部通常包含不止一个目标文件(也就是二进制文件). 值得一提的是,库文件中每个目标文件存储的代码,并非完整的程序,而是一个个实用的功能模块 ...
最新文章
- python画图程序飞机_Python海龟画图工具绘制叮当猫程序
- 不同坐标系下角速度_技术 | 西安80坐标与地方坐标系的转换方法技巧
- 机器学习算法之——隐马尔可夫模型(Hidden Markov Models,HMM) 代码实现
- 转 Django+Bootstrap练习--我的类博客系统开发
- 第十章:Java_IO流
- 看完这篇你就明白,为什么说大部分企业建设数据中台都会失败?
- hypermesh10的安装
- 数据--第34课 - 二叉树的深层性质
- 计算机ppt基础知识题库,计算机二级考试MSOffice考试题库ppt操作题附答案.pdf
- 图易服装PDM产品数据管理系统
- 20个你需要知道的JavaScript简写代码片段
- 计算机专业研究生的读研规划思考------转载
- Android 实现扫一扫功能
- openAL在C++下的易用封装,调用直接播放3D音频,模拟3D音效
- 运维工程师被墨菲定律的各种打脸之DXX问题
- Could not get a resource from the pool 问题解决
- MNN实践[C++版本]
- Linux 机器重启reboot命令
- 网上书店订单流程c语言源代码,网上书店的设计及实现.doc
- 25-30K ☀️|网络工程师职业技巧与经典面试题✨