【问题标题】:Python Regex: Parse key/value pairs from a stringPython Regex:从字符串中解析键/值对
【发布时间】:2020-01-10 16:43:43
【问题描述】:

我正在尝试将如下字符串分解为键/值对列表:

line1 = "keyword1: value1 keyword2: value2 keyword1: value3 keyword3: value4"

我使用正则表达式编写了以下代码来实现该目标:

import re

line1 = "keyword1: value1 keyword2: value2 keyword1: value3 keyword3: value4"

keywords = [ re.escape(k) for k in ['keyword1', 'keyword2', 'keyword3'] ]

any_keyword = '|'.join(keywords)
regex = "(" + any_keyword + "):(.+?)(?:" + any_keyword + "|$)"

print(line1)
print(regex)

for m in re.finditer(regex, line1):
  print(m)

我得到的匹配是

<re.Match object; span=(0, 25), match='keyword1: value1 keyword2'>
<re.Match object; span=(34, 59), match='keyword1: value3 keyword3'>

当然,它们在字符串末尾包含关键字 2 和关键字 3,这样我就不会为这些关键字获得额外的 Match 对象。

我怎样才能收到 4 个匹配项,一行中的每个关键字一个匹配项?

【问题讨论】:

  • 使用 lookahead - regex = "(" + any_keyword + "):(.+?)(?=(?:" + any_keyword + "):|$)"。在关键字后的前瞻中添加: 以使其更安全。见ideone.com/88o81D
  • 是否有机会在 key 和 value 中留出空间?恕我直言,就像@TJC Wood 的答案一样,只需在冒号周围查找非空格对
  • @WiktorStribiżew 您不需要在前瞻组中添加额外的非捕获组。只需(?=key1|key2|key3|$) 就可以了
  • @AdrianShum 因为: 是必要的。每个关键字后跟:,我们不想在值部分中的: 之前停止匹配。
  • @WiktorStribiżew 啊哈!这说得通。我错过了你多余的冒号:)

标签: python regex


【解决方案1】:

您可以使用前瞻而不是非捕获组作为正则表达式中的最后一个模式来提取匹配项,因为非捕获组模式仍然会消耗字符:

import re
line1 = "keyword1: value1 keyword2: value2 keyword1: value3 keyword3: value4"
keywords = ['keyword1', 'keyword2', 'keyword3']
any_keyword = '|'.join(map(re.escape, keywords))
regex = "(" + any_keyword + "):(.+?)(?=(?:" + any_keyword + "):|$)"
print([m.group() for m in re.finditer(regex, line1)])
# => ['keyword1: value1 ', 'keyword2: value2 ', 'keyword1: value3 ', 'keyword3: value4']

Python demo

如果您的键可以包含空格,请确保在定义 any_keyword 模式之前对键进行排序,方法是按长度按降序对它们进行排序,例如sorted(keywords,key=len,reverse=True).

将关键字也作为整个单词进行匹配可能是个好主意:

regex = r"\b(" + any_keyword + r"):(.+?)(?=\b(?:" + any_keyword + "):|$)"

请参阅regex demo。详情:

  • \b - 单词边界
  • (keyword1|keyword2|keyword3) - 第 1 组:关键字替代品
  • : - 一个 : 字符
  • (.+?) - 第 2 组:除换行符之外的任何一个或多个字符尽可能少
  • (?=\b(?:keyword1|keyword2|keyword3):|$) - 一个积极的前瞻,确保在当前位置的右侧,有
    • \b(?:keyword1|keyword2|keyword3): - 列表中的任何关键字,后跟:
    • | - 或
    • $ - 字符串结束。

【讨论】:

    【解决方案2】:

    一种使用re.split的方式:

    line1 = "keyword1: value1 keyword2: value2 keyword1: value3 keyword3: value4"
    l = re.split('(keyword\d+):', line1)[1:]
    
    [(k,v.strip()) for k, v in zip(l[::2], l[1::2])]
    

    输出:

    [('keyword1', 'value1'),
     ('keyword2', 'value2'),
     ('keyword1', 'value3'),
     ('keyword3', 'value4')]
    

    【讨论】:

      【解决方案3】:

      另一种方式

      >>> regex = "(" + any_keyword + "):\s(\w+)"
      >>> pattern = re.compile(regex)
      >>> pattern.search(line1)
      <_sre.SRE_Match object; span=(0, 16), match='keyword1: value1'>
      >>> pattern.findall(line1)
      [('keyword1', 'value1'), ('keyword2', 'value2'), ('keyword1', 'value3'), ('keyword3', 'value4')]
      >>>
      

      【讨论】:

      • 它只支持一个“word-char”字值。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-19
      • 2018-08-12
      • 2014-04-08
      • 2021-10-24
      • 1970-01-01
      • 2013-05-18
      • 2017-08-26
      相关资源
      最近更新 更多