在Python中使用正则表达式作为模板
我有个想法,想用正则表达式的模式作为模板,不知道在Python(3或更新版本)中有没有简单的方法可以做到这一点。
import re
pattern = re.compile("/something/(?P<id>.*)")
pattern.populate(id=1) # that is what I'm looking for
应该得到的结果是
/something/1
相关问题:
6 个回答
3
正则表达式不是用来做这个的,你可以直接用普通的字符串格式化。
>>> '/something/{id}'.format(id=1)
'/something/1'
5
下面是我创建的一个轻量级的类,可以满足你的需求。你只需要写一个正则表达式,就可以用这个表达式来同时匹配字符串和生成字符串。
代码底部有一个小例子,教你怎么使用它。
一般来说,你可以像平常一样构造正则表达式,然后正常使用match和search函数。format函数的用法和string.format类似,用来生成新的字符串。
import re
regex_type = type(re.compile(""))
# This is not perfect. It breaks if there is a parenthesis in the regex.
re_term = re.compile(r"(?<!\\)\(\?P\<(?P<name>[\w_\d]+)\>(?P<regex>[^\)]*)\)")
class BadFormatException(Exception):
pass
class RegexTemplate(object):
def __init__(self, r, *args, **kwargs):
self.r = re.compile(r, *args, **kwargs)
def __repr__(self):
return "<RegexTemplate '%s'>"%self.r.pattern
def match(self, *args, **kwargs):
'''The regex match function'''
return self.r.match(*args, **kwargs)
def search(self, *args, **kwargs):
'''The regex match function'''
return self.r.search(*args, **kwargs)
def format(self, **kwargs):
'''Format this regular expression in a similar way as string.format.
Only supports true keyword replacement, not group replacement.'''
pattern = self.r.pattern
def replace(m):
name = m.group('name')
reg = m.group('regex')
val = kwargs[name]
if not re.match(reg, val):
raise BadFormatException("Template variable '%s' has a value "
"of %s, does not match regex %s."%(name, val, reg))
return val
# The regex sub function does most of the work
value = re_term.sub(replace, pattern)
# Now we have un-escape the special characters.
return re.sub(r"\\([.\(\)\[\]])", r"\1", value)
def compile(*args, **kwargs):
return RegexTemplate(*args, **kwargs)
if __name__ == '__main__':
# Construct a typical URL routing regular expression
r = RegexTemplate(r"http://example\.com/(?P<year>\d\d\d\d)/(?P<title>\w+)")
print(r)
# This should match
print(r.match("http://example.com/2015/article"))
# Generate the same URL using url formatting.
print(r.format(year = "2015", title = "article"))
# This should not match
print(r.match("http://example.com/abcd/article"))
# This will raise an exception because year is not formatted properly
try:
print(r.format(year = "15", title = "article"))
except BadFormatException as e:
print(e)
不过,这里有一些限制:
- format函数只支持关键字参数(你不能像在
string.format中那样使用\1风格的格式化)。 - 在匹配带有子元素的元素时会有一个小bug,比如
RegexTemplate(r'(?P<foo>biz(baz)?)')。这个问题可以通过一些工作来修复。 - 如果你的正则表达式中包含命名组以外的字符类(例如
[a-z123]),我们就不知道该怎么格式化这些内容。
1
如果正则表达式只是一些用预定义字符串连接起来的命名组,你可以把这个正则表达式转换成一个模板字符串,像这样:
from string import Template
def pattern2template(regex, join_string):
tmpl_str = join_string.join(["$"+x for x in regex.groupindex.keys()])
# prepend string to match your case
tmpl_str = join_string + tmpl_str
return Template(tmpl_str)
在你的情况下,这样做会得到:
>>> x = pattern2template(pattern, "/something/")
>>> print(x.template)
/something/$id
>>> print(x.substitute(id="myid"))
/something/myid