Python使用re模块正则式的预编译及pickle方案
项目上线要求当中有言论和昵称的过滤需求, 客户端使用的是python脚本, python脚本中直接利用re模块来进行正则匹配, 一开始的做法是开启游戏后, 每帧编译2条正则式, 无奈运营需求里面100+条略为复杂的正则式, 一条编译起来在pc上都需要80ms, 造成客户端开启时候的卡顿.
解决方案当然是保存re模块编译的正则式结果, 之后开机直接加载就行, 然而需要注意的是re.compile()返回的_sre.SRE_Pattern对象虽然可以使用pickle保存下来, 但是这只是个假象, 实际上它只是保存了你需要编译的正则式内容, 之后实例化回来会重新调用re.compile去重新编译, 对于提高加载速度是毫无帮助的.
查看python re模块的源码可以发现, 实际上re.compile最终调用到的是sre_compile的compile, 而这个sre_compile可以明确的分为prev_compile和post_compile(名字是我起的), 以python2.7的re源代码为例, __version__是2.2.1, 我看了下python3.5的re, __version__是没有变化的. 下面是sre_compile.compile的代码
def compile(p, flags=0): # internal: convert pattern list to internal format if isstring(p): import sre_parse pattern = p p = sre_parse.parse(p, flags) else: pattern = None code = _code(p, flags) # print code # XXX: <fl> get rid of this limitation! if p.pattern.groups > 100: raise AssertionError( "sorry, but this version only supports 100 named groups" ) # map in either direction groupindex = p.pattern.groupdict indexgroup = [None] * p.pattern.groups for k, i in groupindex.items(): indexgroup[i] = k return _sre.compile( pattern, flags | p.pattern.flags, code, p.pattern.groups-1, groupindex, indexgroup )
以#print code为分界, 上面的部分为prev_compile, 之后为post_compile, 前面的预处理产生的p和code最终会传到更底层的_sre.compile接口作为参数, p是由sre_parse.parse()调用产生, 生成的结果是list, 我认为是进行sre_compile的opcode, 通过opcode最终生成code, 而生成code的调用是最为耗时的, 最最重要的是, 这部分p和code都是python的list, 可以通过pickle保存到文件当中, 这样实际上就已经解决了re预编译正则式耗时的问题, 不过由于post_compile最终还会用到你的正则表达式, 所以, 正则式的内容还是必须保留, 并且有一个一一对应的关系, 即你生成的p, code, 需要对应你的pattern.
我们游戏最终的解决方案是把预编译结果生成为一个.py文件, 之后直接在脚本当中import进行post_compile, 下面是具体的代码
import sys sys.path.append( "../../script/data" ) def prev_compile( p, flags = 0 ): import sre_compile, sre_parse if sre_compile.isstring( p ): p = sre_parse.parse( p, flags ) code = sre_compile._code( p, flags ) return p, code def post_compile( pattern, p, code, flags = 0 ): import _sre if p.pattern.groups > 100: raise AssertionError( "sorry, but this version only supports 100 named groups" ) groupindex = p.pattern.groupdict indexgroup = [None] * p.pattern.groups for k, i in groupindex.items(): indexgroup[i] = k return _sre.compile( pattern, flags | p.pattern.flags, code, p.pattern.groups-1, groupindex, indexgroup ) def precompile(): import cPickle as pickle import FilterProp name_p = [] name_code = [] for s in FilterProp.NameFilter: s = s.decode( "utf-8" ) p, code = prev_compile( s ) name_p.append( p ) name_code.append( code ) word_p = [] word_code = [] for s in FilterProp.WordFilter: s = s.decode( "utf-8" ) p, code = prev_compile( s ) word_p.append( p ) word_code.append( code ) with file( "../../script/precompile.py", "w" ) as f: name_p_s = repr( pickle.dumps( name_p ) ) name_code_s = repr( pickle.dumps( name_code ) ) word_p_s = repr( pickle.dumps( word_p ) ) word_code_s = repr( pickle.dumps( word_code ) ) print >> f, "#-*- coding: utf-8 -*-" print >> f, "precompile = [" print >> f, name_p_s, "," print >> f, name_code_s, "," print >> f, word_p_s, "," print >> f, word_code_s print >> f, "]" if __name__ == "__main__": precompile()
FilterProp是具体的正则式配置, 包括了WordFilter和NameFilter, 上面的代码最终生成了一个precompile.py文件, 里面就包括了prev_compile生成的p, code, 之后客户端启动利用这部分结构, 调用post_compile生成_sre.SRE_Pattern对象, 极大的提升了效率, 对于需要预编译大量正则式的需求来说, 这个解决方案还是挺优雅的.
参考资料:
http://stackoverflow.com/questions/4037339/is-there-a-way-to-really-pickle-compiled-regular-expressions-in-python
Python使用re模块正则式的预编译及pickle方案的更多相关文章
- day19 python之re模块正则练习
1.匹配标签 import re ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>" ...
- Android NDK引用预编译的动态链接库
NDK里有个例子: android-ndk-r10/samples/module-exports/jni一看就懂了 ———————————————————————————– 从r5版本开始,就支持预编 ...
- 【转】预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)
用VC++ 2008 编写C语言程序,编译出现错误: 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反) 解决方法: 建工程时 建立空项目 或者在项目设置里关闭预编 ...
- VS2005 MFC 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)
当 Visual C++ 项目启用了预编译头 (Precompiled header) 功能时,如果项目中同时混合有 .c 和 .cpp 源文件,则可能收到 C1853 编译器错误:fatal err ...
- 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)(转)
用VC++ 2008 编写C语言程序,编译出现错误: 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反) 解决方法: 建工程时 建立空项目 或者在项目设置里关闭预编 ...
- 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)转
vs2010的mfc项目中编译c语言出现错误: "...预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)" 解决方法: 建工程时 建立空项目 ...
- VS中c++文件调用c 函数 ,fatal error C1853 预编译头文件来自编译器的早期版本号,或者预编译头为 C++ 而在 C 中使用它(或相反)
出现错误:error C1853: "Debug\ConsoleApplication1.pch"预编译头文件来自编译器的早期版本号.或者预编译头为 C++ 而在 C 中使用它(或 ...
- Python正则式的基本用法
Python正则式的基本用法 1.1基本规则 1.2重复 1.2.1最小匹配与精确匹配 1.3前向界定与后向界定 1.4组的基本知识 2.re模块的基本函数 2.1使用compile加速 2.2 ma ...
- Python开发【模块】:re正则
re模块 序言: re模块用于对python的正则表达式的操作 '.' 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行 '^' 匹配字符开头,若指定flags ...
随机推荐
- Java 基础知识相关好文章
1. 使用简单易懂的例子,分析了equals 和 hashCode 两个方法的异同,尤其中自定义类中对他们的重写,对Set等容器类的在插入时的判断是否相等的影响. http://blog.csdn.n ...
- AppCode 2016.2.3 发布,支持 Swift3 的特性
AppCode 2016.2.3 (build 162.2380.5)发布了,AppCode 是一个全新的 Objective-C.Swift 的集成开发环境,用于帮助开发 Mac.iPhone 和 ...
- shopex 小知识
产品链接: http://www.--/product-172.html 中间的数字代表 sdb_goods 表中 的 goods_id ... 表示数据库里的产品 id. 分类链接: http: ...
- 安全协议系列(三)----CCMP与WPA-PSK
本节讨论 CCM 在 WiFi 中的实际应用 -- CCMP 协议 根据 RFC 3610,完成 CCMP 报文的加解密,需要提供:分组密钥(K).随机数(Nonce).附加认证数据(AAD),这三个 ...
- 2016 - 3 - 12 SQLite的学习之SQL语言入门
1.SQL语句的特点: 1.1 不区分大小写 1.2 每条语句以;结尾 2.SQL语句中常用关键字: select,insert,update,from,create,where,desc,order ...
- JSP自定义标签——调用数据库(通过id号搜索相关信息)
一.创建新表(假设在master数据库下新建) 二.连接数据库 开始-->控制面板-->管理工具-->数据源-->系统DSN-->添加-->SQL Server-- ...
- css 绘制对话框三角符号
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- wampserver 403 禁止访问
解决方法:修改Apache配置文件httpd.conf,注释掉 deny from all:将Allow from 127.0.0.1改为Allow from all
- webclient的简单实用
这是我在项目中调用别人写好的接口口是使用的1.简单的url传参 List<ArticleModel> result = new List<ArticleModel>(); st ...
- android库站点
图片加载库picasso:让本地图片.网络图片的加载变得如此简单. 引导页面的开源框架 guideshow自带小动画,但sdk要求18+ AppIntro界面很简约漂亮 circleindicator ...