19

序章

string モジュールには Template クラスがあり、マッピング オブジェクトを使用して文字列を置換できます。たとえば、次のようになります。

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

たとえば、マッピングにない要素を置換しようとすると、substitute メソッドは 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は気にしません。では、この問題に対する正しいアプローチは何でしょうか?

4

3 に答える 3

7

ドキュメントには、必要な名前付きグループがすべて含まれている限り、パターンを置き換えることができると書かれています。

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 ではないことを示しています。

于 2012-10-07T13:08:25.233 に答える
6

これは簡単な修正です(再帰を使用):

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 $$
于 2012-10-07T10:57:39.360 に答える
-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

于 2016-12-13T05:00:49.513 に答える