使用Python中的模板进行字符串替换

Ern*_*t A 13 python string try-catch

介绍

字符串模块有一个Template类,它允许您使用映射对象在字符串中进行替换,例如:

>>> string.Template('var is $var').substitute({'var': 1})
'var is 1'
Run Code Online (Sandbox Code Playgroud)

例如,如果尝试替换映射中缺少的元素,则替换方法可能引发KeyError异常

>>> string.Template('var is $var and foo is $foo').substitute({'var': 1})
KeyError: 'foo'
Run Code Online (Sandbox Code Playgroud)

或者可能引发ValueError,如果模板字符串无效,例如它包含一个$后跟空格的字符:

>>> string.Template('$ var is $var').substitute({'var': 1})
ValueError: Invalid placeholder in string: line 1, col 1
Run Code Online (Sandbox Code Playgroud)

问题

给定模板字符串和映射,我想确定模板中的所有占位符是否都将被替换.为此,我会尝试进行替换并捕获任何KeyError异常:

def check_substitution(template, mapping):
    try:
        string.Template(template).substitute(mapping)
    except KeyError:
        return False
    except ValueError:
        pass
    return True
Run Code Online (Sandbox Code Playgroud)

但是这不起作用,因为如果模板无效并且引发了ValueError,则不会捕获后续的KeyErrors:

>>> check_substitution('var is $var and foo is $foo', {'var': 1})
False
>>> check_substitution('$ var is $var and foo is $foo', {'var': 1})
True
Run Code Online (Sandbox Code Playgroud)

但我不关心 ValueErrors.那么,解决这个问题的正确方法是什么?

MBa*_*rsi 5

这是一个快速修复(使用递归):

def check_substitution(tem, m):
    try:
        string.Template(tem).substitute(m)
    except KeyError:
        return False
    except ValueError:
        return check_substitution(tem.replace('$ ', '$'), m) #strip spaces after $
    return True
Run Code Online (Sandbox Code Playgroud)

我知道如果$和之间有多个空格,则需要更长的时间var,因此您可以使用正则表达式来改进它。

编辑

逃逸$$$更有意义[感谢@Pedro]这样你就可以赶上ValueError这个语句:

return check_substitution(tem.replace('$ ', '$$ '), m) #escaping $ by $$
Run Code Online (Sandbox Code Playgroud)


jfs*_*jfs 5

文档说,只要它包含所有必需的命名组,您就可以替换该模式:

import re
from string import Template


class TemplateIgnoreInvalid(Template):
    # override pattern to make sure `invalid` never matches
    pattern = r"""
    %(delim)s(?:
      (?P<escaped>%(delim)s) |   # Escape sequence of two delimiters
      (?P<named>%(id)s)      |   # delimiter and a Python identifier
      {(?P<braced>%(id)s)}   |   # delimiter and a braced identifier
      (?P<invalid>^$)            # never matches (the regex is not multilined)
    )
    """ % dict(delim=re.escape(Template.delimiter), id=Template.idpattern)


def check_substitution(template, **mapping):
    try:
        TemplateIgnoreInvalid(template).substitute(mapping)
    except KeyError:
        return False
    else:
        return True
Run Code Online (Sandbox Code Playgroud)

测试

f = check_substitution
assert f('var is $var', var=1)
assert f('$ var is $var', var=1)
assert     f('var is $var and foo is $foo', var=1, foo=2)
assert not f('var is $var and foo is $foo', var=1)
assert     f('$ var is $var and foo is $foo', var=1, foo=2)
assert not f('$ var is $var and foo is $foo', var=1)
# support all invalid patterns
assert f('var is $var and foo is ${foo', var=1)
assert f('var is $var and foo is ${foo', var=1, foo=2) #NOTE: problematic API
assert     f('var is $var and foo is ${foo and ${baz}', var=1, baz=3)
assert not f('var is $var and foo is ${foo and ${baz}', var=1)
Run Code Online (Sandbox Code Playgroud)

它适用于分隔符($)的所有无效出现.

这些示例表明,忽略无效模式会在模板中隐藏简单的拼写错误,因此它不是一个好的API.