如何使用re.DOTALL在多行文本中搜索正则表达式模式?

时间:2018-12-29 19:16:38

标签: python regex

我是一名律师和python初学者,所以我既(a)笨又(b)完全不在我的行列。

我正在尝试将正则表达式模式应用于文本文件。该图案有时可以跨越多条线。我对文本文件中的这些行特别感兴趣:

Considered  and  decided  by  Hemingway,  Presiding  Judge;  Bell, 
Judge;  and \n
 \n
Dickinson, Emily, Judge.

我想逐个寻找,提取并打印出评委的姓名。到目前为止,我的代码如下:

import re
def judges():
    presiding = re.compile(r'by\s*?([A-Z].*),\s*?Presiding\s*?Judge;', re.DOTALL)
    judge2 = re.compile(r'Presiding\s*?Judge;\s*?([A-Z].*),\s*?Judge;', re.DOTALL)
    judge3 = re.compile(r'([A-Z].*), Judge\.', re.DOTALL)
    with open("text.txt", "r") as case:
        for lines in case:
            presiding_match = re.search(presiding, lines)
            judge2_match = re.search(judge2, lines)
            judge3_match = re.search(judge3, lines)
            if presiding_match or judge2_match or judge3_match:
                print(presiding_match.group(1))
                print(judge2_match.group(1))
                print(judge3_match.group(1))
                break

运行它时,我可以得到海明威和贝尔,但是在两次换行之后,我得到了第三位法官的“ AttributeError:'NoneType'对象没有属性'group'”。

经过反复试验,我发现我的代码仅读取第一行(直到“ Bell,Judge;和”为止),然后退出。我以为re.DOTALL可以解决它,但我似乎无法使其工作。

我已经尝试了100万种捕获换行符并获取全部内容的方法,包括re.match,re.DOTALL,re.MULTILINE,“”。join,“”。join(lines.strip()) ,以及我可以扔在墙上的任何其他东西以粘住。

几天后,我鞠躬寻求帮助。感谢您可以做的任何事情。

(顺便说一句,我很难让regex与^和$字符一起使用。它似乎也讨厌在udget3 regex中的。转义。)

3 个答案:

答案 0 :(得分:2)

您要传递单行,因为您要遍历case引用的打开文件。除了单行文本外,从不传递正则表达式。您的正则表达式可以匹配 some 行,但是它们不能同时匹配同一行。

您必须阅读多行内容。如果文件足够小,只需将其读取为一个字符串即可:

with open("text.txt", "r") as case:
    case_text = case.read()

然后将正则表达式应用于该字符串。

或者,您可以单独测试每个匹配对象,而不是成组测试,而仅打印匹配的对象:

if presiding_match:
    print(presiding_match.group(1))
elif judge2_match:
    print(judge2_match.group(1))
elif judge3_match:
    print(judge3_match.group(1))

但是随后您将不得不创建其他逻辑来确定何时完成从文件中读取并退出循环。

请注意,您要匹配的模式不会跨行中断,因此此处实际上不需要DOTALL标志。您确实匹配.*文本,因此如果使用DOTALL,则冒着匹配太多的风险:

>>> import re
>>> case_text = """Considered  and  decided  by  Hemingway,  Presiding  Judge;  Bell, Judge;  and
...
... Dickinson, Emily, Judge.
... """
>>> presiding = re.compile(r'by\s*?([A-Z].*),\s*?Presiding\s*?Judge;', re.DOTALL)
>>> judge2 = re.compile(r'Presiding\s*?Judge;\s*?([A-Z].*),\s*?Judge;', re.DOTALL)
>>> judge3 = re.compile(r'([A-Z].*), Judge\.', re.DOTALL)
>>> presiding.search(case_text).groups()
('Hemingway',)
>>> judge2.search(case_text).groups()
('Bell',)
>>> judge3.search(case_text).groups()
('Considered  and  decided  by  Hemingway,  Presiding  Judge;  Bell, Judge;  and \n\nDickinson, Emily',)

我至少将[A-Z].*替换为[A-Z][^;\n]+,至少 排除匹配的;分号和换行符,并且仅匹配名称至少2个字符长。只需完全删除DOTALL标志:

>>> presiding = re.compile(r'by\s*?([A-Z][^;]+),\s+?Presiding\s+?Judge;')
>>> judge2 = re.compile(r'Presiding\s+?Judge;\s+?([A-Z][^;]+),\s+?Judge;')
>>> judge3 = re.compile(r'([A-Z][^;]+), Judge\.')
>>> presiding.search(case_text).groups()
('Hemingway',)
>>> judge2.search(case_text).groups()
('Bell',)
>>> judge3.search(case_text).groups()
('Dickinson, Emily',)

您可以将三种模式组合为一种:

judges = re.compile(
    r'(?:Considered\s+?and\s+?decided\s+?by\s+?)?'
    r'([A-Z][^;]+),\s+?(?:Presiding\s+?)?Judge[.;]'
)

可以通过.findall()一次性找到您输入中的所有法官:

>>> judges.findall(case_text)
['Hemingway', 'Bell', 'Dickinson, Emily']

答案 1 :(得分:1)

您可以使用re.findall而不是多个re.search,它具有非常短而简单的模式,一次可以找到所有法官:

import re

text = """Considered  and  decided  by  Hemingway,  Presiding  Judge;  Bell, 
Judge;  and \n
 \n
Dickinson, Emily, Judge."""

matches = re.findall(r"(\w+,)?\s(\w+),(\s+Presiding)?\s+Judge", text)
print(matches)

哪些印刷品:

[('', 'Hemingway', '  Presiding'), ('', 'Bell', ''), ('Dickinson,', 'Emily', '')]

所有原始信息都在那里:每个法官的名字,姓氏和“主持人属性”(如果有主持人)。之后,您可以将此原始信息提供给满足您需求的数据结构,例如:

judges = []
for match in matches:
    if match[0]:
        first_name = match[1]
        last_name = match[0]
    else:
        first_name = ""
        last_name = match[1]
    presiding = "Presiding" in match[2]
    judges.append((first_name, last_name, presiding))
print(judges)

哪些印刷品:

[('', 'Hemingway', True), ('', 'Bell', False), ('Emily', 'Dickinson,', False)]

如您所见,现在有一个元组列表,其中第一个元素是名字(如果在文本中指定),第二个元素是姓氏,第三个元素是bool法官是否是审判长。

很显然,该模式适用于您提供的示例。但是,由于(\w+,)?\s(\w+),(\s+Presiding)?\s+Judge是一个非常简单的模式,因此有一些边缘情况需要注意,该模式可能会返回错误的结果:

  • 只有一个名字会被匹配。像Dickinson, Emily Mary这样的名称将导致Mary被检测为姓氏。
  • de Broglie这样的姓氏只会导致Broglie被匹配,因此de将会丢失。
  • ...

您将不得不查看这是否符合您的需求,或者提供有关您的数据问题的更多信息。

答案 2 :(得分:1)

假设您可以一次读取所有文件(即文件不太大)。您可以按以下方式提取法官信息:

import re

regex = re.compile(
    r'decided\s+by\s+(?P<presiding_judge>[A-Za-z]+)\s*,\s+Presiding\s+Judge;'
    r'\s+(?P<judge>[A-Za-z]+)\s*,\s+Judge;'
    r'\s+and\s+(?P<extra_judges>[A-Za-z,\s]+)\s*,\s+Judge\.?',
    re.DOTALL | re.MULTILINE
)

filename = 'text.txt'
with open(filename) as fd:
    data = fd.read()

for match in regex.finditer(data):
    print(match.groupdict())

在示例输入文本文件(text.txt)中看起来like this,输出变为:

{'judge': 'Bell', 'extra_judges': 'Dickinson, Emily', 'presiding_judge': 'Hemingway'}
{'judge': 'Abel', 'extra_judges': 'Lagrange, Gauss', 'presiding_judge': 'Einstein'}
{'judge': 'Dirichlet', 'extra_judges': 'Fourier, Cauchy', 'presiding_judge': 'Newton'}

您也可以在regex101 site

上玩这个游戏