【问题标题】:python: extracting variables from string templatespython:从字符串模板中提取变量
【发布时间】:2017-03-01 16:00:16
【问题描述】:

我熟悉使用Templates 将变量插入字符串的能力,如下所示:

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

我现在想知道的是是否可以反过来做。我想获取一个字符串,并使用模板从中提取值,这样我就有了一些包含提取值的数据结构(最好只是命名变量,但字典很好)。例如:

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

这可能吗?

【问题讨论】:

    标签: python string-formatting


    【解决方案1】:

    那叫regular expressions

    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 -- 字符串是否被接受,minmax 是什么?)。


    更新 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()、可选的块合并以及每个块的独立处理(使用正则表达式和其他方式)。

    【讨论】:

    • 完美!唯一的问题是,是否可以以更简洁的方式命名参数,而不仅仅是使用m.group([position])
    • 如果我们使用这个:m = re.match(r'value is between (?P&lt;min&gt;\d+) and (?P&lt;max&gt;\d+)', string)?这应该只匹配一串连续的数字,对吗?所以字符串“value is between 1 and 2 and 3”将不匹配(最好返回有用的东西,例如None或引发错误
    • 我刚刚尝试过,它返回 'min' 为“5”,'max''7'。如果字符串与正则表达式不完全匹配,有没有办法告诉它失败?
    • @ewok 关于准确度:可以,可以加$(expect the end-of-line):r'value is between (.*) and (.*)$'
    • @ewok 您所描述的似乎还可以(尽管-?\d+(?:\.\d*)? 会稍微合乎逻辑)。我已经更新了提到另一种方法的答案。
    【解决方案2】:

    不可能完全逆转替换。问题是有些字符串是模棱两可的,例如

    value is between 5 and 7 and 10
    

    会有两种可能的解决方案:min = "5", max = "7 and 10"min = "5 and 7", max = "10"

    但是,您也许可以使用正则表达式获得有用的结果:

    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<\1>.*)', pattern)
    match= re.match(pattern, string)
    print(match.groupdict()) # output: {'max': '10', 'min': '5'}
    

    【讨论】:

    • 这看起来可行。你能通过某种方式构建模板来解决歧义吗?我的意思是你能把一个正则表达式传递给基本上说“$min 是与正则表达式\d+ 匹配的最小字符串”的模板。如果你得到“值介于 5 和 7 和 10 之间”,那么你只会得到一个错误(这会很有帮助)
    • @ewok,是的,如果您有关于变量的其他信息,例如“min 是一个数字”,那么您可以通过使用适当的正则表达式来减少歧义。但总会有一些模版和字符串模棱两可。例如$number1$number2,或类似的。这完全取决于模板的格式。如果你很幸运,你不会有任何问题。
    【解决方案3】:

    行为驱动开发的behave module 提供了几种不同的指定机制,parsing templates.

    根据模板的复杂性和应用程序的其他需求,您可能会发现其中一个最有用。 (另外,你可以窃取他们预先编写的代码。)

    【讨论】:

      【解决方案4】:

      你可以使用 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'}

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-27
        • 1970-01-01
        • 1970-01-01
        • 2022-01-06
        • 2019-07-22
        • 2013-08-28
        • 2020-12-27
        • 1970-01-01
        相关资源
        最近更新 更多