我正在尝试扫描文档并确定文档各部分的开始和结束位置。有时,该文档有一个目录,列出了我不想捕获TOC的页码,因为它不能识别文档的一部分。我有一段时间一直搞乱这个问题而且我一直坚持下去。我似乎无法避免使用行号
从内容列表中捕获行这是正则表达式
verbose_item_pattern_3 = re.compile(r"""
^ # begin match at newline
\t* # 0-or-more tabspace
[ ]* # 0-or-more blank space
I # a capital I
[tT][eE][mM] # one character from each of the three sets this allows for unknown case
\t* # 0-or-more tabspace
[ ]* # 0-or-more blankspace
\d{1,2} # 1-or-2 digits
[.]? # 0-or-1 literal .
\(? # 0-or-1 literal open paren
[a-e]? # 0-or-1 letter in the range a-e
\)? # 0-or-1 closing paren
.* # any number of unknown characters so we can have words and punctuation
[^0-9] # anything but [0-9]
$ # 1 newline character
""", re.VERBOSE|re.MULTILINE)
这是一个我不想捕获的行的例子
test_string='\nItem 6. TITLE ITEM 6..................................................25\n'
以下是我想要捕获的示例
test_string='\nItem 6. TITLE ITEM 6 maybe other words here who knows \n'
但是当我跑步时
re.findall(verbose_item_pattern_3,test_string)
结果是
['Item 6. TITLE ITEM 6..................................................25\n']
现在我觉得有趣的是,如果我的测试字符串是这个
test_string='PART I\nItem 1. TITLE ITEM 1...................................................1\nItem 2. TITLE ITEM 2..................................................21\n'
然后运行它 re.findall(verbose_item_pattern_3,test_string)
结果更接近我想要的但仍然不正确
['Item 2. TITLE ITEM 2..................................................21\n']
不应该捕获任何东西
答案 0 :(得分:2)
你的正则表达式匹配因为三件事。
.*
吃了整条线,所以你的最后一个条件[^0-9]
将永远无法承受,那是因为:[^0-9]
,因此即使该行以数字结尾,[^0-9]
也能成功匹配。最小的改变是在最后使用负面的后视:
verbose_item_pattern_3 = re.compile(r"""
^ # start-of-line
\t* # 0-or-more tabspace
[ ]* # 0-or-more blank space
I # a capital I
[tT][eE][mM] # one character from each of the three sets this allows for unknown case
\t* # 0-or-more tabspace
[ ]* # 0-or-more blankspace
\d{1,2} # 1-or-2 digits
[.]? # 0-or-1 literal .
\(? # 0-or-1 literal open paren
[a-e]? # 0-or-1 letter in the range a-e
\)? # 0-or-1 closing paren
.* # any number of unknown characters so we can have words and punctuation
$ # end-of-line
(?<![0-9]) # NOT preceded by a decimal digit (via look-behind)
""", re.VERBOSE|re.MULTILINE)
请注意,^
而不是$
实际上都不匹配换行符。它们在(^
)或之前的位置($
)后面的位置匹配位置。换行符本身永远不会成为比赛的一部分。
由于这个原因,我已将他们的评论更改为更精确的start-of-line
和end-of-line
。
另请注意,即使在$
之后,我仍然可以应用负面观察。这样做有助于防止回溯,使正则表达式更快。</ p>
答案 1 :(得分:2)
如果我理解正确,您希望示例字符串不匹配,因为该行中的最后一个字符是数字,而您的正则表达式以[^0-9]$
结尾。
行为不正确的原因是$
将在\n
之前匹配,但也会在字符串的最后匹配。最后发生的事情是.*
与数字匹配,然后[^0-9]
匹配\n
,并在字符串末尾匹配$
。请考虑以下示例,该示例使用捕获组来说明其工作原理:
>>> re.match(r'(.*)([^0-9])$', '...12\n').groups()
('...12', '\n')
要解决此问题,您可以通过将换行符更改为[^0-9]
来阻止[^0-9\n]
匹配换行符:
verbose_item_pattern_3 = re.compile(r"""
^ # begin match at newline
\t* # 0-or-more tabspace
[ ]* # 0-or-more blank space
I # a capital I
[tT][eE][mM] # one character from each of the three sets this allows for unknown case
\t* # 0-or-more tabspace
[ ]* # 0-or-more blankspace
\d{1,2} # 1-or-2 digits
[.]? # 0-or-1 literal .
\(? # 0-or-1 literal open paren
[a-e]? # 0-or-1 letter in the range a-e
\)? # 0-or-1 closing paren
.* # any number of unknown characters so we can have words and punctuation
[^0-9\n] # anything but [0-9] and line breaks
$ # 1 newline character
""", re.VERBOSE|re.MULTILINE)
示例(使用上面的正则表达式):
>>> verbose_item_pattern_3.findall('\nItem 6. TITLE ITEM 6.....25\n')
[]
>>> verbose_item_pattern_3.findall('\nItem 6. TITLE ITEM 6.....\n')
['Item 6. TITLE ITEM 6.....']