【问题标题】:Parse a string with key expressions使用键表达式解析字符串
【发布时间】:2020-10-15 13:04:25
【问题描述】:

我正在尝试解析具有这种格式的字符串:

sample = '<STATUS="OK" VERSION="B" MESSAGE="Connected in demo mode"><timestamp="1602765370" id="123">'

这样给定一个键我就可以得到关联的值,例如:

parser('STATUS', sample)  # 'OK'
parser('MESSAGE', sample) # 'Connected in demo mode'

我尝试过使用 re:

import re
def parser(key, string):
    return re.search(f'(?<={key}=)\S+', string).group()

但第一个示例的结果为'"OK"',第二个示例的结果为'"Connected'。如何避免检索引号并获取与每个值关联的完整字符串?提前致谢。

【问题讨论】:

  • 这实际上应该是 XML 吗?如果是这样,您是否尝试过使用 XML 解析器?
  • 我收到这个数据抛出了 requests.request('GET', url) 并且它以字符串的形式出现(比我的示例大,但格式为 '<...><...><...>')。我尝试使用 xml.etree.ElementTree.fromstring(sample, parser=parser) 但我收到此错误:'xml.etree.ElementTree.ParseError: not well-formed (invalid token)'。我对 xml 不熟悉,所以我没有进一步采用这种方法。你认为 python xml 解析器是比 re 更好的方法吗?

标签: python python-3.x parsing python-re


【解决方案1】:

如果您要检索的值保证是双引号字符串,那么下面的定义应该可以工作。它允许在字符串中使用转义引号,当键不存在时不会引发异常,并且如果您的键是现有键的后缀,则不会给出误报。

import re
def parser(key, string):
    m = re.search(fr'(?<![A-Z]){key}="(.*?)(?<!\\)"', string)
    if m:
        return m.group(1) 

正则表达式的第一部分 (?&lt;![A-Z]) 是一个否定的后向表达式,仅当 A-Z 范围内的字符在您的键之前没有匹配时才匹配。它确保您在使用作为现有键后缀的键(例如 US,它是 STATUS 的后缀)查询字符串时不会得到误报。

返回不带引号的值只需将引号包含在正则表达式中,但在您检索的正则表达式组之外。这就是表达式"(.*?)(?&lt;!\\)" 中发生的情况。与您要检索的值关联的正则表达式组是(.*?)(?&lt;!\\) 表达式是一个否定的后向表达式,可确保末尾的 " 仅在其前面没有反斜杠时匹配。

例子:

sample = r'<STATUS="OK" VERSION="B" MESSAGE="User said \"hi!\""><timestamp="1602765370" id="123">'

[parser('STATUS', sample),
 parser('US', sample),
 parser('MESSAGE', sample)]                                     

输出:

['OK', None, 'User said \\"hi!\\"']

【讨论】:

    【解决方案2】:

    这将返回给定键后"" 内的所有内容。

    import re
    
    def get_value(key, string):
        return re.search(f'{key} *= *"(.*?)"', string).group(1)
    

    添加一些错误处理,使其更加健壮。

    【讨论】:

      【解决方案3】:

      假设这不是 xml/html(sample 对这些无效),您可以使用此方法,而无需使用正则表达式。这有点令人费解,但它有效 - 至少在这种情况下:

      keys = ['STATUS','MESSAGE']
      targets = sample.split('><')[0].split('"')
      for k,v in zip(targets[::2],targets[1::2]):
          for key in keys:
              if key in k:
                  print(k.replace('<','').replace('=','').strip(),'---',v)
      

      输出:

      STATUS --- OK
      MESSAGE --- Connected in demo mode
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-12-10
        • 2010-11-22
        • 2011-10-14
        • 1970-01-01
        • 2023-03-26
        • 1970-01-01
        相关资源
        最近更新 更多