英文:
Python regex positive lookahead cannot split correctly
问题
[^a-z]+
behaves like a lazy match because it's using the +
quantifier after a negated character class. This means it will match the shortest possible sequence of characters that do not fall within the range of lowercase letters (a to z). To make it behave as a greedy match, you can use [^a-z]+
followed by +
without the negated character class, like this: [^a-z]+
.
Here's the corrected regular expression for splitting the text by sections:
re.split(r'\n(?=[A-Z\d])', text)
This regex uses a positive lookahead assertion to split the text at line breaks (\n
) that are followed by an uppercase letter or a digit, which should correctly split the text into sections as you expected.
英文:
I've text consisting of sections. In each section:
- The title is in uppercase and may span multiple lines
- The body may have acronyms, so we cannot assume that uppercase words mark the start of each section
There may be zero or multiple line breaks between sections.
Example
import re
text = """
Lorem ipsum
THIS SECTION IS A SHORT STORY
1 Hello world
2 Bye bye
Side comment
NEXT SECTION SPANS 200
YEARS AND MANY COUNTRIES!
3 Joe Bloggs attended a NATO summit
4 John Doe heard...
THIS SECTION HAS NO
LINE BREAK / SPACE FROM
THE PREVIOUS ONE
5 Alice thought...
6 Bob visited...
""".strip()
re.split("\n(?=[^a-z]+\n+[a-z\d])", text)
I expected it to split the text by sections like this:
["Lorem ipsum\n",
"THIS SECTION IS A SHORT STORY\n1 Hello world\n2 Bye bye\nSide comment\n\n",
"NEXT SECTION SPANS 200\nYEARS AND MANY COUNTRIES!\n\n3 Joe Bloggs attended a NATO summit\n4 John Doe heard...",
"THIS SECTION HAS NO\nLINE BREAK / SPACE FROM\nTHE PREVIOUS ONE\n\n5 Alice thought...\n6 Bob visited..."]
Instead, Python splits up each section as follows, which seems to contradict the lookahead assertion:
["Lorem ipsum",
"",
"THIS SECTION IS A SHORT STORY\n1 Hello world\n2 Bye bye\nSide comment",
"",
"",
"NEXT SECTION SPANS 200",
"YEARS AND MANY COUNTRIES!\n\n3 Joe Bloggs attended a NATO summit\n4 John Doe heard...",
"THIS SECTION HAS NO",
"LINE BREAK / SPACE FROM",
"THE PREVIOUS ONE\n\n5 Alice thought...\n6 Bob visited..."]
Questions
Why does [^a-z]+
behave like a lazy match instead of greedy match?
What's the correct solution?
答案1
得分: 1
更新的示例
我们可以添加一个回顾来匹配双\n
(或者在不需要尾随\n
的情况下拆分\n\n
),并在字符集中包含数字。
re.split(r"(?<=\n)\n(?=[A-Z0-9 ]+\n)", text)
或者 (?<=\n)\n(?= *[A-Z][A-Z0-9 ]*\n)
来强制至少有一个初始大写字母。
输出:
['Lorem ipsum\n',
'THIS SECTION IS A SHORT STORY\n1 Hello world\n2 Bye bye\n',
'THIS SECTION SPANS 200\nYEARS AND MANY COUNTRIES\n3 Joe Bloggs saw...\n4 John Doe heard...\n',
'THIS SECTION IS ALSO A\nLONG STORY ABOUT EVERYTHING\nSINCE 1669\n\n5 Alice thought...\n6 Bob visited...']
使用循环
import re
out = ['']
prev_header = True
for line in text.splitlines():
if line:
header = bool(re.fullmatch('[^a-z]+', line))
if header and not prev_header:
out.append(line+'\n')
else:
out[-1] += line+'\n'
prev_header = header
输出:
['Lorem ipsum\n',
'THIS SECTION IS A SHORT STORY\n1 Hello world\n2 Bye bye\nSide comment\n',
'NEXT SECTION SPANS 200\nYEARS AND MANY COUNTRIES!\n3 Joe Bloggs attended a NATO summit\n4 John Doe heard...\n',
'THIS SECTION HAS NO\nLINE BREAK / SPACE FROM\nTHE PREVIOUS ONE\n5 Alice thought...\n6 Bob visited...\n']
英文:
updated example
We can add a lookbehind to match a double \n
(or split on \n\n
if you don't need the trailing \n
), and include digits in the set of characters.
re.split(r"(?<=\n)\n(?=[A-Z0-9 ]+\n)", text)
Or (?<=\n)\n(?= *[A-Z][A-Z0-9 ]*\n)
to force at least one initial uppercase.
Output:
['Lorem ipsum\n',
'THIS SECTION IS A SHORT STORY\n1 Hello world\n2 Bye bye\n',
'THIS SECTION SPANS 200\nYEARS AND MANY COUNTRIES\n3 Joe Bloggs saw...\n4 John Doe heard...\n',
'THIS SECTION IS ALSO A\nLONG STORY ABOUT EVERYTHING\nSINCE 1669\n\n5 Alice thought...\n6 Bob visited...']
using a loop
import re
out = ['']
prev_header = True
for line in text.splitlines():
if line:
header = bool(re.fullmatch('[^a-z]+', line))
if header and not prev_header:
out.append(line+'\n')
else:
out[-1] += line+'\n'
prev_header = header
Output:
['Lorem ipsum\n',
'THIS SECTION IS A SHORT STORY\n1 Hello world\n2 Bye bye\nSide comment\n',
'NEXT SECTION SPANS 200\nYEARS AND MANY COUNTRIES!\n3 Joe Bloggs attended a NATO summit\n4 John Doe heard...\n',
'THIS SECTION HAS NO\nLINE BREAK / SPACE FROM\nTHE PREVIOUS ONE\n5 Alice thought...\n6 Bob visited...\n']
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论