使用正则表达式作为Python的模板

使用正则表达式作为Python的模板,python,regex,templates,Python,Regex,Templates,我想使用正则表达式模式作为模板,不知道在Python(3或更高版本)中是否有一种方便的方法 这不是正则表达式的用途,您可以使用普通的字符串格式 >>> '/something/{id}'.format(id=1) '/something/1' 将编译保存到替换之后: pattern = re.compile("/something/(?P<%s>.*)" % 1) pattern=re.compile(“/something/(?P.*)”%1) 下面是我创建的

我想使用正则表达式模式作为模板,不知道在Python(3或更高版本)中是否有一种方便的方法


这不是正则表达式的用途,您可以使用普通的字符串格式

>>> '/something/{id}'.format(id=1)
'/something/1'

将编译保存到替换之后:

pattern = re.compile("/something/(?P<%s>.*)" % 1)
pattern=re.compile(“/something/(?P.*)”%1)

下面是我创建的一个轻量级类,它可以满足您的需求。您可以编写一个正则表达式,并将该表达式用于匹配字符串和生成字符串

代码底部有一个关于如何使用它的小示例

通常,您可以正常构造正则表达式,并正常使用
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)
    
重新导入
regex_type=type(重新编译(“”)
#这并不完美。如果正则表达式中有括号,它将中断。
re\u term=re.compile(r“(?[\w\d]+)\>(?P[^\)]*))
类BadFormatException(异常):
通过
类RegexTemplate(对象):
定义初始值(self,r,*args,**kwargs):
self.r=re.compile(r,*args,**kwargs)
定义报告(自我):
返回“%self.r.pattern”
def匹配(自身、*args、**kwargs):
''正则表达式匹配函数''
返回self.r.match(*args,**kwargs)
def搜索(self、*args、**kwargs):
''正则表达式匹配函数''
返回self.r.search(*args,**kwargs)
def格式(自身,**kwargs):
''以与string.Format类似的方式格式化此正则表达式。
仅支持真正的关键字替换,不支持组替换。“”
pattern=self.r.模式
def更换(m):
name=m.group('name')
reg=m.group('regex')
val=kwargs[名称]
如果未重新匹配(注册,val):
引发BadFormatException(“模板变量“%s”有一个值”
%s的,与正则表达式%s不匹配。%(名称、val、reg))
返回值
#regex子函数完成了大部分工作
值=re_term.sub(替换,模式)
#现在我们有了逃逸的特殊角色。
返回re.sub(r“\\([.\(\)\[\]])”,r“\1”,值)
def编译(*args,**kwargs):
返回RegexTemplate(*args,**kwargs)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
#构造一个典型的URL路由正则表达式
r=RegexTemplate(r“http://example\.com/(?P\d\d\d)/(?P\w+)
印刷品(r)
#这应该匹配
打印(r.match)(“http://example.com/2015/article"))
#使用URL格式生成相同的URL。
打印(r.format(year=“2015”,title=“article”))
#这不应该匹配
打印(r.match)(“http://example.com/abcd/article"))
#这将引发异常,因为年份格式不正确
尝试:
打印(r.格式(年份=“15”,标题=“文章”))
除BadFormat例外情况外,如e:
打印(e)
有一些限制:

  • format函数仅适用于关键字参数(不能使用
    \1
    中的
    string.format
    样式格式)
  • 还有一个错误是将元素与子元素匹配,例如,
    RegexTemplate(r'(?Pbiz(baz)?))
    。这可以通过一些工作来纠正
  • 如果正则表达式包含命名组之外的字符类(例如,
    [a-z123]
    ),我们将不知道如何格式化这些字符类

对于非常简单的情况,最简单的方法可能是用格式字段替换命名的捕获组

下面是一个基本的验证器/格式化程序:

重新导入
从functools导入部分
unescape=partial(重新编译(r'\\(.)).sub,r'\1')
namedgroup=partial(重新编译(r'\(\?P.*?\)).sub,r'{\1}')
类别模具:
定义初始(自我,模式):
self.pattern=re.compile(模式)
self.template=unescape(namedgroup(pattern))
def格式(自身,**值):
尝试:
返回self.template.format(**值)
除KeyError外,如e:
从None引发TypeError(f'缺少参数:{e}')
def搜索(self,字符串):
尝试:
返回self.pattern.search(string.groupdict())
除属性错误外:
从“无”提升值错误(字符串)
例如,要实例化电话号码的验证程序/格式化程序,格式为
(XXX)YYY-ZZZZ

template = r'\((?P<area>\d{3})\)\ (?P<prefix>\d{3})\-(?P<line>\d{4})'
phonenum = Mould(template)

但这是一个非常基本的框架,它忽略了许多正则表达式特性(例如,lookarounds或非捕获组)。如果需要,事情很快就会变得一团糟。在这种情况下,另一种方法是:从模板生成模式,虽然更详细,但可能更灵活,更不容易出错

下面是基本的验证器/格式化程序(
.search()
.format()
是相同的):

导入字符串
进口稀土
FMT=string.Formatter()
类别模具:
定义初始化(自身、模板、**kwargs):
self.template=模板
self.pattern=self.make_模式(模板,**kwargs)
@静力学方法
def make_图案(模板,**kwargs):
模式=“”
#对于模板中的每个字段,添加到模式中
对于FMT.parse(模板)中的文本、字段*:
#前面转义的文本
模式+=重新转义(文本)
如果字段:
#命名的正则表达式捕获组
模式+=f'(?P{kwargs[field]})
#XXX:如果最后一个字段后面有文本,
#解析器将再迭代一次,
#因此,“如果字段”
返回重新编译(模式)
实例化:

template='({area}){prefix}-{line}'
content=dict(area=r'\d{3}',prefix=r'\d{3}',line=r'\d{4})
phonenum=Mold(模板,**内容)
执行
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)
    
template = r'\((?P<area>\d{3})\)\ (?P<prefix>\d{3})\-(?P<line>\d{4})'
phonenum = Mould(template)
>>> phonenum.search('(333) 444-5678')
{'area': '333', 'prefix': '444', 'line': '5678'}

>>> phonenum.format(area=111, prefix=555, line=444)
(111) 555-444