Python 带有组织模式文件的多行正则表达式

Python multiline regex with org-mode files

我想使用正则表达式从 Emacs 组织模式文件中提取某些部分,这些部分是简单的文本文件。这些组织文件中的条目以 * 开头,有时这些条目确实具有属性。一个简短的例子可以在下面找到:

import re

orgfiletest = """
* headline 0
* headline 1
  :PROPERTIES:
  :KEY: lala
  :END:
* headline 2
* headline 3
  :PROPERTIES:
  :KEY: lblb
  :END:
"""

我想提取所有具有属性的条目;提取的条目应包括这些属性。所以,我想收到以下文字:

* headline 1
  :PROPERTIES:
  :KEY: lala
  :END:

* headline 3
  :PROPERTIES:
  :KEY: lblb
  :END:

我是从这样开始的

re.findall(r"\*.*\s:END:", orgfiletest, re.DOTALL)

但这也包括headline 0headline 2,它们没有任何属性。我的下一次尝试是利用环顾四周,但无济于事。任何帮助深表感谢!

适用于我的更新/解决方案:

感谢所有帮助我找到解决方案的人!为了将来参考,我包含了更新的 MWE 和适用于我的正则表达式:

import re
orgfiletest = """
* headline 0
  more text 
* headline 1
  :PROPERTIES:
  :KEY: lala
  :END:
* headline foo 2
** bar 3
  :PROPERTIES:
  :KEY: lblb
  :FOOBAR: lblb
  :END:
* new headline
  more text
"""

re.findall(r"^\*+ .+[\r\n](?:(?!\*)\s*:.+[\r\n]?)+", orgfiletest, re.MULTILINE)

有几种可能性,包括非正则表达式解决方案。
正如您特别要求的那样:

^\*\ headline\ \d+[\r\n] # look for "* headline digit(s) and newline
(?:(?!\*).+[\r\n]?)+     # followed by NOT a newline at the beginning
                         # ... anything else including newlines afterwards
                         # ... at least once

参见 a demo on regex101.com(注意修饰符 xm!)


Python 中,这将是:

import re

rx = re.compile(r'''
            ^\*\ headline\ \d+[\r\n] 
            (?:(?!\*).+[\r\n]?)+
            ''', re.VERBOSE | re.MULTILINE)

print(rx.findall(orgfiletest))


非正则表达式 方式可能是(使用 itertools):

from itertools import groupby

result = {}; key = None
for k, v in groupby(
        orgfiletest.split("\n"), 
        lambda line: line.startswith('* headline')):
    if k:
        item = list(v)
        key = item[len(item)-1]
    elif key is not None:
        result[key] = list(v)

print(result)
# {'* headline 1': ['  :PROPERTIES:', '  :KEY: lala', '  :END:'], '* headline 3': ['  :PROPERTIES:', '  :KEY: lblb', '  :END:', '']}

这有一个缺点,即以例如开头的行* headline abc* headliner*** 也会被使用。老实说,我会选择 regex 解决方案。

我想你可以这样做。仅匹配包含 PROPERTIES

的记录

(?ms)^\*(?:(?!^\*).)*?PROPERTIES(?:(?!^\*).)*

https://regex101.com/r/oZcos0/1

已解释

 (?ms)                 # Inline modifiers:  Multi-line, Dot-all
 ^ \*                  # Start record: BOL plus *
 (?:                   # Minimal matching
      (?! ^ \* )            # Not a new record
      . 
 )*?
 PROPERTIES            # Up to prop
 (?:                   # Max matching up to begin new record
      (?! ^ \* )            # Not a new record
      . 
 )*

尝试制作可读的正则表达式:

^\*\sheadline(?:(?!^\*\sheadline).)*:END:$

^\*\sheadline -> 已知该项目是这样开始的。

(?:(?!^\*\sheadline).)* -> 匹配任何内容,只要它不包括我们如何知道新项目开始。

:END:$ -> 它在行尾包含一个已知的结束语句。

Working demo.