【问题标题】:Extract key-value pairs from text containing brackets (log files)从包含括号的文本(日志文件)中提取键值对
【发布时间】:2019-05-20 01:53:52
【问题描述】:

假设这个字符串:

[aaa   ] some text here [bbbb3 ] some other text here [cc    ] more text

我想要一个像这样的键值对:

Key      Value
aaa      some text here  
bbbb3    some other text here  
cc       more text

或者像这样的 pandas DataFrame

aaa            | bbbb3                |cc
-------------------------------------------------
some text here | some other text here | more text
next line      | .....                | .....

我尝试了一个正则表达式,例如:r'\[(.{6})\]\s(.*?)\s\[',但这不起作用。

【问题讨论】:

  • 试试r'\[([^][]*?)\s*]\s*(.*?)(?=\s*\[|$)',见this demo
  • 这是要进入 pandas DataFrame 吗?
  • @K.Madden:这不是在回答我的问题。我需要正则表达式来捕获或拆分我的字符串。
  • @PatrickArtner:抱歉打错了,更新示例

标签: python regex python-3.x pandas


【解决方案1】:

使用re.findall,并将感兴趣的区域提取到列中。然后,您可以根据需要去除空格。

既然您提到您愿意将其读入 DataFrame,您可以将这项工作留给 pandas。

import re
matches = re.findall(r'\[(.*?)\](.*?)(?=\[|$)', text)

df = (pd.DataFrame(matches, columns=['Key', 'Value'])
        .apply(lambda x: x.str.strip()))

df
     Key                 Value
0    aaa        some text here
1  bbbb3  some other text here
2     cc             more text

或者(回复:编辑),

df = (pd.DataFrame(matches, columns=['Key', 'Value'])
        .apply(lambda x: x.str.strip())
        .set_index('Key')
        .transpose())

Key               aaa                 bbbb3         cc
Value  some text here  some other text here  more text

模式匹配大括号内的文本,然后是大括号外的文本,直到下一个左大括号。

\[      # Opening square brace 
(.*?)   # First capture group
\]      # Closing brace
(.*?)   # Second capture group
(?=     # Look-ahead 
   \[   # Next brace,
   |    # Or,
   $    # EOL
)

【讨论】:

  • 非常感谢,也感谢正则表达式的解释!是否可以将键用作列名?
  • @JohnDoe Re:edit, pd.DataFrame(matches, columns=['Key', 'Value']).apply(...).set_index('Key').T
  • 这不是我的意思。我会尝试更好地解释它。现在列是KeyValue,我希望看到第一个捕获组中的column=['aaa', 'bbbb3','cc'] 和第二个捕获组中的值。关键值在这里令人困惑。我刚刚在这里提到以字典结尾,并且可能以 DataFrame 结尾
  • @JohnDoe 是的,想通了。请参阅我的编辑以显示如何以该格式加载它。转置后,列就是键。
  • 好的,谢谢。让我有点困惑,因为我仍然将键值视为索引
【解决方案2】:

试试这个正则表达式,它在命名组捕获中捕获您的键和值。

\[\s*(?P<key>\w+)+\s*]\s*(?P<value>[^[]*\s*)

说明:

  • \[ --> 由于[ 具有定义字符集的特殊含义,因此需要对其进行转义并且匹配文字[
  • \s* --> 在不需要的键的一部分的预期键之前占用任何前面的空格
  • (?P&lt;key&gt;\w+)+ --> 形成一个 key 命名组,捕获一个或多个单词 [a-zA-Z0-9_] 字符。我使用 \w 来保持简单,因为 OP 的字符串只包含字母数字字符,否则应该使用 [^]] 字符集来捕获方括号内的所有内容作为键。
  • \s* --> 占用预期的密钥捕获之后的任何后续空间,不需要密钥的一部分
  • ] --> 匹配不需要转义的文字 ]
  • \s* --> 占用任何不需要成为 value 一部分的前面空间
  • (?P&lt;value&gt;[^[]*\s*) --> 形成一个value 命名组,捕获任何字符异常[,此时它停止捕获并将捕获的值分组到命名组value

Demo

Python 代码,

import re
s = '[aaa   ] some text here [bbbb3 ] some other text here [cc    ] more text'

arr = re.findall(r'\[\s*(?P<key>\w+)+\s*]\s*(?P<value>[^[]*\s*)', s)
print(arr)

输出,

[('aaa', 'some text here '), ('bbbb3', 'some other text here '), ('cc', 'more text')]

【讨论】:

    【解决方案3】:

    您可以使用re.split() 最小化所需的正则表达式并输出到字典。例如:

    import re
    
    text = '[aaa   ] some text here [bbbb3 ] some other text here [cc    ] more text'
    
    # split text on "[" or "]" and slice off the first empty list item
    items = re.split(r'[\[\]]', text)[1:]
    
    # loop over consecutive pairs in the list to create a dict
    d = {items[i].strip(): items[i+1].strip() for i in range(0, len(items) - 1, 2)}
    
    print(d)
    # {'aaa': 'some text here', 'bbbb3': 'some other text here', 'cc': 'more text'}
    

    【讨论】:

    • 在@PatrickArtner 的回答中查看更好的基于str.split() 的方法(不需要正则表达式)。
    【解决方案4】:

    这里真的不需要正则表达式 - 简单的字符串拆分就可以了:

    s = "[aaa   ] some text here [bbbb3 ] some other text here [cc    ] more text"    
    
    parts = s.split("[")  # parts looks like: ['', 
                          #                    'aaa   ] some text here ',
                          #                    'bbbb3 ] some other text here ', 
                          #                    'cc    ] more text'] 
    d = {}
    # split parts further
    for p in parts:
        if p.strip():
            key,value = p.split("]")            # split each part at ] and strip spaces
            d[key.strip()] = value.strip()      # put into dict
    
    # Output:
    form = "{:10} {}"
    print( form.format("Key","Value"))
    
    for i in d.items():
          print(form.format(*i))
    

    输出:

    Key        Value
    cc         more text
    aaa        some text here
    bbbb3      some other text here
    

    用于格式化的 Doku:


    几乎是 1-liner:

    d = {hh[0].strip():hh[1].strip() for hh in (k.split("]") for k in s.split("[") if k)}  
    

    【讨论】:

      【解决方案5】:

      你可以使用finditer:

      import re
      
      s = '[aaa   ] some text here [bbbb3 ] some other text here [cc    ] more text'
      
      pattern = re.compile('\[(\S+?)\s+\]([\s\w]+)')
      result = [(match.group(1).strip(), match.group(2).strip()) for match in pattern.finditer(s)]
      print(result)
      

      输出

      [('aaa', 'some text here'), ('bbbb3', 'some other text here'), ('cc', 'more text')]
      

      【讨论】:

      • @coldspeed 更新了答案!
      【解决方案6】:

      使用 RegEx,您可以找到 key,value 对,将它们存储在字典中,然后打印出来:

      import re
      
      mystr = "[aaa   ] some text here [bbbb3 ] some other text here [cc    ] more text"
      
      a = dict(re.findall(r"\[([A-Za-z0-9_\s]+)\]([A-Za-z0-9_\s]+(?=\[|$))", mystr))
      
      for key, value in a.items():
          print key, value
      
      # OUTPUT: 
      # aaa     some text here 
      # cc      more text
      # bbbb3   some other text here 
      

      RegEx 匹配 2 个组:
      第一组是用方括号括起来的所有字符、数字和空格,第二组是前面有一个封闭方括号,后面是一个开放方括号的所有字符、数字和空格或行尾

      第一组:\[([A-Za-z0-9_\s]+)\]
      第二组:([A-Za-z0-9_\s]+(?=\[|$))

      请注意,在第二组中,我们有一个positive lookahead(?=\[|$)。如果没有正向前瞻,字符将被消耗,并且下一组将找不到起始方括号。

      findall 然后返回一个元组列表:[(key1,value1), (key2,value2), (key3,value3),...]
      元组列表可以立即转换为字典:dict(my_tuple_list)。

      一旦你有了你的字典,你就可以用你的键/值对做你想做的事:)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-22
        • 1970-01-01
        • 2018-09-08
        • 1970-01-01
        • 2021-07-21
        • 1970-01-01
        相关资源
        最近更新 更多