项目上线要求当中有言论和昵称的过滤需求, 客户端使用的是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方案的更多相关文章

  1. day19 python之re模块正则练习

    1.匹配标签 import re ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>" ...

  2. Android NDK引用预编译的动态链接库

    NDK里有个例子: android-ndk-r10/samples/module-exports/jni一看就懂了 ———————————————————————————– 从r5版本开始,就支持预编 ...

  3. 【转】预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)

    用VC++ 2008 编写C语言程序,编译出现错误: 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反) 解决方法: 建工程时 建立空项目 或者在项目设置里关闭预编 ...

  4. VS2005 MFC 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)

    当 Visual C++ 项目启用了预编译头 (Precompiled header) 功能时,如果项目中同时混合有 .c 和 .cpp 源文件,则可能收到 C1853 编译器错误:fatal err ...

  5. 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)(转)

    用VC++ 2008 编写C语言程序,编译出现错误: 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反) 解决方法: 建工程时 建立空项目 或者在项目设置里关闭预编 ...

  6. 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)转

    vs2010的mfc项目中编译c语言出现错误: "...预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)" 解决方法: 建工程时 建立空项目  ...

  7. VS中c++文件调用c 函数 ,fatal error C1853 预编译头文件来自编译器的早期版本号,或者预编译头为 C++ 而在 C 中使用它(或相反)

    出现错误:error C1853: "Debug\ConsoleApplication1.pch"预编译头文件来自编译器的早期版本号.或者预编译头为 C++ 而在 C 中使用它(或 ...

  8. Python正则式的基本用法

    Python正则式的基本用法 1.1基本规则 1.2重复 1.2.1最小匹配与精确匹配 1.3前向界定与后向界定 1.4组的基本知识 2.re模块的基本函数 2.1使用compile加速 2.2 ma ...

  9. Python开发【模块】:re正则

    re模块 序言: re模块用于对python的正则表达式的操作 '.' 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行 '^' 匹配字符开头,若指定flags ...

随机推荐

  1. Oracle存储过程中异常Exception的捕捉和处理

    Oracle存储过程中异常的捕捉和处理 CREATE OR REPLACE Procedure Proc_error_process ( v_IN in Varchar2, v_OUT Out Var ...

  2. css中的1px并不总等于设备的1px(高分辨率不等 低分辨等)

    在css中我们一般使用px作为单位,在桌面浏览器中css的1个像素往往都是对应着电脑屏幕的1个物理像素,这可能会造成我们的一个错觉,那就是css中的像素就是设备的物理像素.但实际情况却并非如此,css ...

  3. reinstall ubuntu

    flickering mouse issue http://askubuntu.com/questions/310341/do-graphics-drivers-for-intel-hd-4600-e ...

  4. 利用NABCD模型进行竞争性需求分析

    微博的NABCD模型 N-Need:毫无疑问,当今的中国普通民众是有这点需求的,在上个世纪中国民众的休闲娱乐方式更多的停留在以电视传媒为主的娱乐方式,而进入21世纪以来中国民众的娱乐中心向互联网转移, ...

  5. initcall调用顺序

    在解释initcall调用顺序, 先要理一下编译链接的知识. 每个.o文件都有自己的代码段, 数据段(存放初始化的全局变量), bss段(即未初始化的数据段) 在ld链接器将各.o文件的代码段和数据段 ...

  6. DES带IV向量加密解密工具

    链接:http://pan.baidu.com/s/1kVAV80J  密码:sgys 鉴于网上的DES加密解密都是不带IV向量的 我就自制了一个带IV向量的DES加密解密的小工具 © 2016-20 ...

  7. BZOJ 1537 二维偏序

    #include <iostream> #include <cstring> #include <cstdio> #include <algorithm> ...

  8. Java记事本编译

    配置环境: 在“系统变量”栏下执行三项操作:①新建“Java_Home”,设置其值为 JDK所在的绝对路径,如果你的事刚才的默认路径,那值为:C:\Program Files\Java\jdk1.7. ...

  9. 基于vue2.0的分页组件开发

    今天安排的任务是写基于vue2.0的分页组件,好吧,我一开始是觉得超级简单的,但是越写越写不出来,写的最后乱七八糟的都不知道下句该写什么了,所以重新捋了思路,小结一下- 首先写组件需要考虑: 要从父组 ...

  10. .htaccess详解及.htaccess参数说明【转】

    目录(?)[-] htaccess 详解 htaccess rewrite 规则详细说明 RewriteEngine OnOff RewriteBase URL-path RewriteCond Te ...