Python 的正则表达式:在边界内查找所有内容
RegEx with Python: findall inside a boundry
我有一个字符串,可以通过以下内容(预期的额外空格)来说明:
"words that don't matter START some words one some words two some words three END words that don't matter"
为了获取 START 和 END ['some words one', some words two', 'some words three']
之间的每个子字符串,我编写了以下代码:
result = re.search(r'(?<=START).*?(?=END)', string, flags=re.S).group()
result = re.findall(r'(\(?\w+(?:\s\w+)*\)?)', result)
是否可以用一个正则表达式实现这一点?
理论上,您可以将第二个正则表达式包装在 ()*
中,然后将其放入第一个正则表达式中。这将捕获边界内所有出现的内部表达式。不幸的是,Python 实现仅保留多次匹配的组的最后一个匹配项。据我所知,保留组的所有匹配项的唯一实现是 .NET 实现。很遗憾,这不是适合您的解决方案。
另一方面,为什么您不能简单地保留现有的两步法?
编辑:
您可以使用在线正则表达式工具比较我描述的行为。
模式:(\w+\s*)*
输入:aaa bbb ccc
例如用 https://pythex.org/ and http://regexstorm.net/tester 试试。
你会看到 Python returns 一个 match/group 是 ccc
而 .NET returns </code> 是三个 <em>captures</em> <code>aaa, bbb, ccc
.
Edit2:正如@Jan 所说,还有更新的 regex
模块支持多重捕获。我完全忘记了。
这是我们可以使用 re.split
的理想情况,正如@PeterE 提到的那样,以规避只能访问最后捕获的组的问题。
import re
s=r'"words that don\'t matter START some words one some words two some words three END words that don\'t matter" START abc a bc c END'
print('\n'.join(re.split(r'^.*?START\s+|\s+END.*?START\s+|\s+END.*?$|\s{2,}',s)[1:-1]))
启用 re.MULTILINE
/re.M
标志,因为我们正在使用 ^
和 $
。
输出
some words one
some words two
some words three
abc
a bc c
使用较新的 regex
模块,您可以一步完成:
(?:\G(?!\A)|START)\s*\K
(?!\bEND\b)
\w+\s+\w+\s+\w+
这看起来很复杂,但分解后,它说:
(?:\G(?!\A)|START) # look for START or the end of the last match
\s*\K # whitespaces, \K "forgets" all characters to the left
(?!\bEND\b) # neg. lookahead, do not overrun END
\w+\s+\w+\s+\w+ # your original expression
在 Python
中看起来像:
import regex as re
rx = re.compile(r'''
(?:\G(?!\A)|START)\s*\K
(?!\bEND\b)
\w+\s+\w+\s+\w+''', re.VERBOSE)
string = "words that don't matter START some words one some words two some words three END words that don't matter"
print(rx.findall(string))
# ['some words one', 'some words two', 'some words three']
此外,请参阅 a demo on regex101.com。
我有一个字符串,可以通过以下内容(预期的额外空格)来说明:
"words that don't matter START some words one some words two some words three END words that don't matter"
为了获取 START 和 END ['some words one', some words two', 'some words three']
之间的每个子字符串,我编写了以下代码:
result = re.search(r'(?<=START).*?(?=END)', string, flags=re.S).group()
result = re.findall(r'(\(?\w+(?:\s\w+)*\)?)', result)
是否可以用一个正则表达式实现这一点?
理论上,您可以将第二个正则表达式包装在 ()*
中,然后将其放入第一个正则表达式中。这将捕获边界内所有出现的内部表达式。不幸的是,Python 实现仅保留多次匹配的组的最后一个匹配项。据我所知,保留组的所有匹配项的唯一实现是 .NET 实现。很遗憾,这不是适合您的解决方案。
另一方面,为什么您不能简单地保留现有的两步法?
编辑: 您可以使用在线正则表达式工具比较我描述的行为。
模式:(\w+\s*)*
输入:aaa bbb ccc
例如用 https://pythex.org/ and http://regexstorm.net/tester 试试。
你会看到 Python returns 一个 match/group 是 ccc
而 .NET returns </code> 是三个 <em>captures</em> <code>aaa, bbb, ccc
.
Edit2:正如@Jan 所说,还有更新的 regex
模块支持多重捕获。我完全忘记了。
这是我们可以使用 re.split
的理想情况,正如@PeterE 提到的那样,以规避只能访问最后捕获的组的问题。
import re
s=r'"words that don\'t matter START some words one some words two some words three END words that don\'t matter" START abc a bc c END'
print('\n'.join(re.split(r'^.*?START\s+|\s+END.*?START\s+|\s+END.*?$|\s{2,}',s)[1:-1]))
启用 re.MULTILINE
/re.M
标志,因为我们正在使用 ^
和 $
。
输出
some words one
some words two
some words three
abc
a bc c
使用较新的 regex
模块,您可以一步完成:
(?:\G(?!\A)|START)\s*\K
(?!\bEND\b)
\w+\s+\w+\s+\w+
这看起来很复杂,但分解后,它说:
(?:\G(?!\A)|START) # look for START or the end of the last match
\s*\K # whitespaces, \K "forgets" all characters to the left
(?!\bEND\b) # neg. lookahead, do not overrun END
\w+\s+\w+\s+\w+ # your original expression
在
Python
中看起来像:
import regex as re
rx = re.compile(r'''
(?:\G(?!\A)|START)\s*\K
(?!\bEND\b)
\w+\s+\w+\s+\w+''', re.VERBOSE)
string = "words that don't matter START some words one some words two some words three END words that don't matter"
print(rx.findall(string))
# ['some words one', 'some words two', 'some words three']
此外,请参阅 a demo on regex101.com。