使用键表达式解析字符串

Parse a string with key expressions

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

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/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

此 returns 给定键后 "" 内的所有内容。

import re

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

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

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

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

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

返回不带引号的值只是在正则表达式中包含引号但在您检索的正则表达式组之外的问题。这就是表达式 "(.*?)(?<!\)" 中发生的情况。与您要检索的值关联的正则表达式组是 (.*?)(?<!\) 表达式是一个负数 look-behind,它确保末尾的 " 仅在前面没有反斜杠时才匹配。

示例:

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!\"']