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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12768107/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 11:50:48  来源:igfitidea点击:

String substitutions using templates in Python

pythonstringtry-catch

提问by Ernest A

Introduction

介绍

The string module has a Template class, that lets you make substitutions in a string using a mapping object, for instance:

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

>>> string.Template('var is $var').substitute({'var': 1})
'var is 1'

The substitute method may raise a KeyError exception, if an attempt is made to substitute an element that is missing from the mapping, for instance

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

>>> string.Template('var is $var and foo is $foo').substitute({'var': 1})
KeyError: 'foo'

or may raise a ValueError, if the template string is invalid, e.g. it contains a $character followed by a space:

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

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

The Problem

问题

Given a template string and a mapping, I want to determine whether all place-holders in the template would be substituted. For this, I would try to make the substitution and catch any KeyError exception:

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

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

But this doesn't work, because if the template is invalid and a ValueError is raised, subsequent KeyErrors aren't caught:

但这不起作用,因为如果模板无效并且引发了 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

but I don't careabout ValueErrors. So, what would be the right approach to this problem?

但我不在乎ValueErrors。那么,解决这个问题的正确方法是什么?

采纳答案by jfs

The docs saythat you can replace the pattern as long as it contains all necessary named groups:

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

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

Tests

测试

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)

It works for all invalid occurences of the delimiter ($).

它适用于所有无效的分隔符 ( $)。

The examples show that ignoring invalid patterns conceals simple typos in the template so it is not a good API.

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

回答by MBarsi

This is a Quick Fix (Using recursion):

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

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

I Know its take a longer time if there is more than One Space between $and var, so you may improve it by using Regular Expression.

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

EDIT

编辑

escaping $into $$makes more sense [ Thanks @Pedro ] so you can catch ValueErrorby this statement:

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

return check_substitution(tem.replace('$ ', '$$ '), m) #escaping $ by $$

回答by knowingpark

Python will not do string substitution over multiple lines

Python 不会在多行上进行字符串替换

If you have this string

如果你有这个字符串

criterion = """
    <criteria>
    <order>{order}</order>
      <body><![CDATA[{code}]]></body>
    </criteria>
"""

criterion.format(dict(order="1",code="Hello")

results in:

结果是:

KeyError: 'order'

A solution is to use the string.Template module

一个解决方案是使用 string.Template 模块

from string import Template

criterion = """
    <criteria>
    <order>$order</order>
      <body><![CDATA[$code]]></body>
    </criteria>
"""

Template(criterion).substitute(dict(order="1",code="hello")

NOTE: you have to prefix the keywords with a $ not wrap them in {}

注意:您必须在关键字前加上 $,而不是将它们包裹在 {} 中

output is:

输出是:

 <criteria>
    <order>1</order>
      <body><![CDATA[hello]]></body>
    </criteria>

Full docs are: https://docs.python.org/2/library/string.html#template-strings

完整文档是:https: //docs.python.org/2/library/string.html#template-strings