在不考虑新行的情况下拆分多个分隔符上的字符串



我有一个表示对话转折的字符串,如下所示:

s = "person alpha:nHow are you today?nnperson beta:nI'm fine, thank you.nnperson alpha:nWhat's up?nnperson beta:nNot much, just hanging around."

在纯文本中,它看起来如下。

person alpha:
How are you today?
person beta:
I'm fine, thank you.
person alpha:
What's up?
person beta:
Not much, just hanging around.

现在,我想分割person alphaperson beta上的字符串,这样得到的列表如下:

[人物阿尔法:你今天怎么样?,人物贝塔:我很好,谢谢

我尝试了以下方法

import re
res = re.split('person alpha |person beta |*|n', s)

但结果如下:

['person alpha:', 'How are you today?', '', 'person beta:', "I'm fine, thank you.", '', 'person alpha:', "What's up?", '', 'person beta:', 'Not much, just hanging around.']

我的正则表达式出了什么问题?

'person alpha |person beta |*|n'

这种尝试有很多问题。首先:传递给re.split的模式应该匹配分隔符,而不是项。也就是说,文本中不会的部分出现在结果列表中的任何项目内,而是位于两者之间:

>>> re.split('delimiter', 'foodelimiterbardelimiterbaz')
['foo', 'bar', 'baz']

除此之外,文本中的person alphaperson beta后面始终跟有冒号,而不是空格;所以这些选择永远都不匹配。*将匹配一个文字星号(因为它是转义的);但没有明显的理由去寻找。n是此正则表达式中的一个换行符;碰巧的是,Python的正则表达式引擎会接受正则表达式字符串中的一个换行符,并将其视为反斜杠-n转义序列——但重要的是要理解,这里通常有两层转义。

不管怎样,关键是:这个正则表达式与输入中的一个换行符相匹配,还与其他一些从未出现的可能性相匹配。然后,re.split返回这些匹配之间的事物列表,即输入的行。


现在,我想将字符串拆分为person alpha和person beta,这样得到的列表如下

一般来说,我们说"在X〃上分割;意思是X是分隔符。由于person alphaperson beta都应该出现在结果的开头,所以它们不是分隔符。

相反,我们要查找的分隔符是这些短语之前的单词边界。

当我们寻找该分隔符时,我们希望确保它后面跟着个人标识符(这样我们就知道它是分隔符),但正则表达式需要不匹配该标识符。为了解决这个问题,我们使用了积极的前瞻性。

我们想要:一个单词边界(b),person有一个正向前瞻((?=...)),后面跟着一个人名,后面跟着冒号。为了简化,我假设人名可以是单词person之后的任何内容,并且不应该局限于alphabeta

因此,lookahead应该匹配person.*:,这意味着整个lookahead子句是(?=person.*:)。整个正则表达式是b(?=person.*:),为此我们使用原始字符串,这样Python就可以从字面上理解反斜杠,并将其传递给正则表达式引擎(它将自己解释b序列,而不是Python的)。

把它放在一起:

>>> re.split(r'b(?=person.*:)', s)
['', 'person alpha:nHow are you today?nn', "person beta:nI'm fine, thank you.nn", "person alpha:nWhat's up?nn", 'person beta:nNot much, just hanging around.']

请注意,在输出列表的开头留下了一个空字符串。这是因为我们要查找的分隔符位于输入的开头。re.split为我们提供了分隔符之前、之间和之后的内容。在我们的例子中,第一个分隔符之前是一个空字符串。

为了避免这种情况,一个简单的方法是重新考虑这个问题。我们将搜索对话框项目本身,而不是搜索对话框项目之间的点(它们之间没有任何文本并不重要)。

每个项看起来都像person,一个名称:,任何文本和两个换行符——作为正则表达式person.*?:.*?nn。因为正则表达式现在将实际匹配文本,而不仅仅是向前看,所以使用不情愿的限定符(该正则表达式中的?s)非常重要。

然后,我们将该正则表达式与re.findall一起使用。它需要再次使用原始字符串,我们还需要使用正则表达式的re.DOTALL选项,以告诉正则表达式引擎.应该能够匹配换行符。(否则,正则表达式将失败,因为在到达双换行符之前,第二个.*?将与每个对话框项中的单个换行符不匹配。)

把它放在一起:

>>> re.findall(r'person.*?:.*?nn', s, flags=re.DOTALL)
['person alpha:nHow are you today?nn', "person beta:nI'm fine, thank you.nn", "person alpha:nWhat's up?nn"]

根据需求的解释方式,还有许多其他编写正则表达式的方法。例如,与其用re.split方法匹配单词边界(b),不如寻找行首锚(^)。事实上,除了前瞻模式之外,我们不需要任何东西,只要我们不介意将文本拆分到它所说的person someone:的任何位置(即使它不在一行(^)的开头,也不在一个单词(b)的开头或其他任何位置)。另一方面,使用re.findall方法,我们可以通过前瞻性检查来将nn从匹配中排除。

但是,如果项目总是由nn分隔,并且实际上没有必要验证它们是否以个人标签开头,我们可以按照该文字序列拆分文本这甚至不需要正则表达式

>>> s.split('nn')
['person alpha:nHow are you today?', "person beta:nI'm fine, thank you.", "person alpha:nWhat's up?", 'person beta:nNot much, just hanging around.']

您的模式只匹配一个换行符,就像在示例数据中一样,有一个冒号:在alpha:beta:之后,所以您基本上是在换行符上进行拆分,以产生这些结果。

您可以使用前瞻(?=断言而不是匹配来重新拆分字符串,并删除空字符串并剥离结果。

import re
s = "person alpha:nHow are you today?nnperson beta:nI'm fine, thank you.nnperson alpha:nWhat's up?nnperson beta:nNot much, just hanging around."
pattern = r"(?=^person (?:alpha|beta):)"
res = [v.rstrip() for v in re.split(pattern, s, 0, re.M) if v]
print(res)

输出

['person alpha:nHow are you today?', "person beta:nI'm fine, thank you.", "person alpha:nWhat's up?", 'person beta:nNot much, just hanging around.']

请参阅Python演示。


使用re.findall,您可以将所有行与至少一个字符匹配,断言下一行不是以人物模式开头的:

import re
s = "person alpha:nHow are you today?nnperson beta:nI'm fine, thank you.nnperson alpha:nWhat's up?nnperson beta:nNot much, just hanging around."
pattern = r"^person (?:alpha|beta):n(?:(?!person (?:alpha|beta):).+(?=n|$))*"
print(re.findall(pattern, s, re.M))

输出

['person alpha:nHow are you today?', "person beta:nI'm fine, thank you.", "person alpha:nWhat's up?", 'person beta:nNot much, just hanging around.']

请参阅Python演示。

re.findall()将是合适的方法。使用re.DOTALL标志来匹配新行。正则表达式中的CCD_;α";或";β";,.*?是一个匹配任何字符的非贪婪模式,(?=nnperson|$)是一个正向前瞻,它断言只有在紧接着新的行字符和人物字符串的情况下匹配才成功

import re
s = "person alpha:nHow are you today?nnperson beta:nI'm fine, thank you.nnperson alpha:nWhat's up?nnperson beta:nNot much, just hanging around."
match = re.findall(r"(person (alpha|beta):n.*?(?=nnperson|$))", s, re.DOTALL)
result = list(map(lambda x: x[0], match ))
# or
# result = [x[0] for x in match]
print(result)

输出:

["个人阿尔法:\n今天怎么样?","个人贝塔:\n很好,谢谢。","人阿尔法:\n怎么样?";人物阿尔法:";以及";人类:使用以下正则表达式:
match = re.findall(r"((person alpha|Human):n.*?(?=nn(person alpha|Human)|$))", s, re.DOTALL)

要匹配两者:请使用以下正则表达式:

match = re.findall(r"((person|Human) (alpha|beta):n.*?(?=nn(person|Human)|$))", s, re.DOTALL)

最新更新