字符串模块有一个 Template 类,它允许您使用映射对象在字符串中进行替换,例如:
>>> string.Template('var is $var').substitute({'var': 1})
'var is 1'
例如,如果尝试替换映射中缺少的元素,替代方法可能会引发 KeyError 异常
>>> string.Template('var is $var and foo is $foo').substitute({'var': 1})
KeyError: 'foo'
如果模板字符串无效,例如,可能会引发 ValueError它包含一个
$
字符,后跟一个空格:
>>> string.Template('$ var is $var').substitute({'var': 1})
ValueError: Invalid placeholder in string: line 1, col 1
给定模板字符串和映射,我想确定模板中的所有占位符是否都会被替换。为此,我会尝试进行替换并捕获任何 KeyError 异常:
def check_substitution(template, mapping):
try:
string.Template(template).substitute(mapping)
except KeyError:
return False
except ValueError:
pass
return True
但这不起作用,因为如果模板无效并且引发 ValueError,则不会捕获后续的 KeyError:
>>> check_substitution('var is $var and foo is $foo', {'var': 1})
False
>>> check_substitution('$ var is $var and foo is $foo', {'var': 1})
True
但我不关心 ValueErrors。那么,解决这个问题的正确方法是什么?
文档说,只要它包含所有必要的命名组,您就可以替换该模式:
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
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)
它适用于所有无效的分隔符 (
$
)。
这些示例表明,忽略无效模式会隐藏模板中的简单拼写错误,因此它不是一个好的 API。
这是一个快速修复(使用递归):
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
我知道如果
$
和 var
之间有超过一个空格,它需要更长的时间,所以你可以使用正则表达式来改进它。
编辑
将
$
转义为 $$
更有意义[感谢@Pedro],这样你就可以通过以下语句捕获 ValueError
:
return check_substitution(tem.replace('$ ', '$$ '), m) #escaping $ by $$
好的形式是: string.Template('$$ var is $var').substitute({'var': 1}),有必要将 $ 加倍。
样品: print(string.Template('$$ var 是 $var').substitute({'var': 1})) $ var 是 1
Python 不会进行多行字符串替换
如果你有这个字符串
criterion = """
<criteria>
<order>{order}</order>
<body><![CDATA[{code}]]></body>
</criteria>
"""
criterion.format(dict(order="1",code="Hello")
结果:
KeyError: 'order'
解决方案是使用 string.Template 模块
from string import Template
criterion = """
<criteria>
<order>$order</order>
<body><![CDATA[$code]]></body>
</criteria>
"""
Template(criterion).substitute(dict(order="1",code="hello")
注意:您必须在关键字前添加 $ 前缀,而不是将它们包含在 {} 中
输出为:
<criteria>
<order>1</order>
<body><![CDATA[hello]]></body>
</criteria>
完整文档为:https://docs.python.org/2/library/string.html#template-strings