python: извлечение переменных из шаблонов строк

Я знаком с возможностью вставки переменных в строку с помощью Шаблоны, например:

Template('value is between $min and $max').substitute(min=5, max=10)

теперь я хочу знать, можно ли сделать обратное. Я хочу взять строку и извлечь из нее значения с помощью шаблона, чтобы у меня была некоторая структура данных (предпочтительно просто именованные переменные, но dict в порядке), которая содержит извлеченные значения. Например:

>>> string = 'value is between 5 and 10'
>>> d = Backwards_template('value is between $min and $max').extract(string)
>>> print d
{'min': '5', 'max':'10'}

это возможно?

4 ответов


это называется регулярные выражения:

import re
string = 'value is between 5 and 10'
m = re.match(r'value is between (.*) and (.*)', string)
print(m.group(1), m.group(2))

выход:

5 10

обновление 1. имена могут быть даны группам:

m = re.match(r'value is between (?P<min>.*) and (?P<max>.*)', string)
print(m.group('min'), m.group('max'))

но эта функция используется не часто, так как обычно достаточно проблем с более важным аспектом: как захватить именно то, что вы хотите (в этом конкретном случае это не имеет большого значения, но даже здесь: что, если строка value is between 1 and 2 and 3 -- если строка будет принята и что за min и max?).


обновление 2. вместо того, чтобы делать точное регулярное выражение, иногда проще комбинировать регулярные выражения и "регулярный" код следующим образом:

m = re.match(r'value is between (?P<min>.*) and (?P<max>.*)', string)
try:
    value_min = float(m.group('min'))
    value_max = float(m.group('max'))
except (AttributeError, ValueError):  # no match or failed conversion
    value_min = None
    value_max = None

этот комбинированный подход особенно стоит помнить, когда ваш текст состоит из многих фрагментов (например, фраз в кавычках разных типов) для обработки: в сложных случаях сложнее определить одно регулярное выражение для обработки как разделителей, так и содержимого фрагментов чем определить несколько шагов типа text.split(), необязательное слияние кусков и независимая обработка каждого куска (с использованием регулярных выражений и других средств).


невозможно полностью отменить замену. Проблема в том, что некоторые строки неоднозначны, например

value is between 5 and 7 and 10

будет иметь два возможных решения: min = "5", max = "7 and 10" и min = "5 and 7", max = "10"

однако вы можете достичь полезных результатов с помощью regex:

import re

string = 'value is between 5 and 10'
template= 'value is between $min and $max'

pattern= re.escape(template)
pattern= re.sub(r'\$(\w+)', r'(?P<>.*)', pattern)
match= re.match(pattern, string)
print(match.groupdict()) # output: {'max': '10', 'min': '5'}

на behave модуль для управляемого поведением развития предоставляет несколько различных механизмов для указания и шаблоны парсинга.

в зависимости от сложности ваших шаблонов и других потребностей вашего приложения вы можете найти тот или иной наиболее полезный. (Кроме того, вы можете украсть их предварительно написанный код.)


вы можете использовать модуль difflib для сравнения двух строк и извлечения нужной информации.

https://docs.python.org/3.6/library/difflib.html

например:

import difflib

def backwards_template(my_string, template):
    my_lib = {}
    entry = ''
    value = ''

    for s in difflib.ndiff(my_string, template):
        if s[0]==' ':
            if entry != '' and value != '':
                my_lib[entry] = value 
                entry = ''
                value = ''   
        elif s[0]=='-':
            value += s[2]
        elif s[0]=='+':
            if s[2] != '$':
                entry += s[2]

    # check ending if non-empty
    if entry != '' and value != '':
        my_lib[entry] = value

    return my_lib

my_string = 'value is between 5 and 10'
template = 'value is between $min and $max'     

print(backwards_template(my_string, template))

дает: {'min': '5', 'max': '10'}