码迷,mamicode.com
首页 > 编程语言 > 详细

Python使用re模块正则式的预编译及pickle方案

时间:2016-01-17 14:42:12      阅读:636      评论:0      收藏:0      [点我收藏+]

标签:

  项目上线要求当中有言论和昵称的过滤需求, 客户端使用的是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的代码

 1 def compile(p, flags=0):
 2     # internal: convert pattern list to internal format
 3 
 4     if isstring(p):
 5         import sre_parse
 6         pattern = p
 7         p = sre_parse.parse(p, flags)
 8     else:
 9         pattern = None
10 
11     code = _code(p, flags)
12 
13     # print code
14 
15     # XXX: <fl> get rid of this limitation!
16     if p.pattern.groups > 100:
17         raise AssertionError(
18             "sorry, but this version only supports 100 named groups"
19             )
20 
21     # map in either direction
22     groupindex = p.pattern.groupdict
23     indexgroup = [None] * p.pattern.groups
24     for k, i in groupindex.items():
25         indexgroup[i] = k
26 
27     return _sre.compile(
28         pattern, flags | p.pattern.flags, code,
29         p.pattern.groups-1,
30         groupindex, indexgroup
31         )

  以#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, 下面是具体的代码

 1 import sys
 2 sys.path.append( "../../script/data" )
 3 
 4 def prev_compile( p, flags = 0 ):
 5     import sre_compile, sre_parse
 6     if sre_compile.isstring( p ):
 7         p = sre_parse.parse( p, flags )
 8 
 9     code = sre_compile._code( p, flags )
10     return p, code
11 
12 def post_compile( pattern, p, code, flags = 0 ):
13     import _sre
14     if p.pattern.groups > 100:
15         raise AssertionError( "sorry, but this version only supports 100 named groups" )
16 
17     groupindex = p.pattern.groupdict
18     indexgroup = [None] * p.pattern.groups
19     for k, i in groupindex.items():
20         indexgroup[i] = k
21 
22     return _sre.compile(
23         pattern, flags | p.pattern.flags, code,
24         p.pattern.groups-1,
25         groupindex, indexgroup
26         )
27 
28 def precompile():
29     import cPickle as pickle
30     import FilterProp
31     name_p = []
32     name_code = []
33     for s in FilterProp.NameFilter:
34         s = s.decode( "utf-8" )
35         p, code = prev_compile( s )
36         name_p.append( p )
37         name_code.append( code )
38 
39     word_p = []
40     word_code = []
41     for s in FilterProp.WordFilter:
42         s = s.decode( "utf-8" )
43         p, code = prev_compile( s )
44         word_p.append( p )
45         word_code.append( code )
46 
47     with file( "../../script/precompile.py", "w" ) as f:
48         name_p_s = repr( pickle.dumps( name_p ) )
49         name_code_s = repr( pickle.dumps( name_code ) )
50         word_p_s = repr( pickle.dumps( word_p ) )
51         word_code_s = repr( pickle.dumps( word_code ) )
52         print >> f, "#-*- coding: utf-8 -*-"
53         print >> f, "precompile = ["
54         print >> f, name_p_s, ","
55         print >> f, name_code_s, ","
56         print >> f, word_p_s, ","
57         print >> f, word_code_s
58         print >> f, "]"
59 
60 if __name__ == "__main__":
61     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方案

标签:

原文地址:http://www.cnblogs.com/zhoufanscut/p/5137126.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!