【问题标题】:Split string by position not character按位置而不是字符拆分字符串
【发布时间】:2015-04-07 16:29:32
【问题描述】:

我们知道anchorsword boundarieslookaround 匹配一个位置,而不是匹配一个字符。
是否可以使用正则表达式(特别是在 python 中)通过上述方式之一拆分字符串?

例如考虑以下字符串:

"ThisisAtestForchEck,Match IngwithPosition." 

所以我想要以下结果(以大写字母开头但前面没有空格的子字符串):

['Thisis', 'Atest', 'Forch' ,'Eck,' ,'Match Ingwith' ,'Position.']

如果我通过分组进行拆分,我会得到:

>>> re.split(r'([A-Z])',s)
['', 'T', 'hisis', 'A', 'test', 'F', 'orch', 'E', 'ck,', 'M', 'atchingwith', 'P', 'osition.']

这是环视的结果:

>>> re.split(r'(?<=[A-Z])',s)
['ThisisAtestForchEck,MatchingwithPosition.']
>>> re.split(r'((?<=[A-Z]))',s)
['ThisisAtestForchEck,MatchingwithPosition.']
>>> re.split(r'((?<=[A-Z])?)',s)
['ThisisAtestForchEck,MatchingwithPosition.']

请注意,如果我想通过以大写开头并以空格开头的子字符串进行拆分,例如:

['Thisis', 'Atest', 'Forch' ,'Eck,' ,'Match ', Ingwith' ,'Position.']

我可以使用re.findall,即:

>>> re.findall(r'([A-Z][^A-Z]*)',s)
['Thisis', 'Atest', 'Forch', 'Eck,', 'Match ', 'Ingwith', 'Position.']

但是第一个例子呢:可以用re.findall 解决吗?

【问题讨论】:

  • 您不能使用split 来执行此操作,因为它使用正则表达式来定义分隔符,这些分隔符被认为与被拆分的元素分开。在上一个示例中使用 re.findall 有什么问题?
  • @Barmar 是的,我知道,但正如我所说的那样,我可以将第一个示例与re.finall 分开吗?我认为split 对于此类任务更灵活!

标签: python regex split


【解决方案1】:

re.findall的方式:

re.findall(r'(?:[A-Z]|^[^A-Z\s])[^A-Z\s]*(?:\s+[A-Z][^A-Z]*)*',s)

当您决定将您的方法从 split 更改为 findall 时,第一项工作包括重新制定您的要求:“我想在每个大写字母上拆分字符串,而不是前面有空格”=>“我想要查找一个或多个以大写字母开头的空格分隔的子字符串,但字符串开头除外(如果字符串不以大写字母开头)"

【讨论】:

  • 这是一个好方法,也很棘手!你能用look-around吗?
【解决方案2】:
 (?<!\s)(?=[A-Z])

您可以使用它与正则表达式模块进行拆分,因为 re 不支持在 0 宽度断言处拆分。

import regex
x="ThisisAtestForchEck,Match IngwithPosition."
print regex.split(r"(?<![\s])(?=[A-Z])",x,flags=regex.VERSION1)

print [i for i in regex.split(r"(?<![\s])(?=[A-Z])",x,flags=regex.VERSION1) if i]

查看演示。

https://regex101.com/r/sJ9gM7/65

【讨论】:

  • @Kasra 啊!!!!!!你可以做pip install regex。它是一个python模块。只是它不是自己来的
  • 是的,正如我所说,这是一个很好的答案,但我必须等待更多;)如果我没有得到答案,我会接受你的答案!现在加 1!
【解决方案3】:

我知道由于结果的元组性质,这可能不太方便。但我认为这个findall 能找到你需要的东西:

re.findall(r'((?<!\s)[A-Z]([^A-Z]|(?<=\s)[A-Z])*)', s)
## returns [('Thisis', 's'), ('Atest', 't'), ('Forch', 'h'), ('Eck,', ','), ('Match Ingwith', 'h'), ('Position.', '.')]

这可用于以下列表推导以提供所需的输出:

[val[0] for val in re.findall(r'((?<!\s)[A-Z]([^A-Z]|(?<=\s)[A-Z])*)', s)]
## returns ['Thisis', 'Atest', 'Forch', 'Eck,', 'Match Ingwith', 'Position.']

这是一个使用split的hack:

re.split(r'((?<!\s)[A-Z]([^A-Z]|(?<=\s)[A-Z])*)', s)[1::3]
## returns ['Thisis', 'Atest', 'Forch', 'Eck,', 'Match Ingwith', 'Position.']

【讨论】:

  • @Kasra ...嗨,如果这个答案有问题,请告诉我,我很想学习
  • 没有错,这是一个很好的答案,谢谢!但我不想使用slicing
  • @Kasra 好的。如果您有兴趣,我刚刚编辑了我的答案,以使用列表理解从 findall 返回所需的列表。
【解决方案4】:

尝试使用此模式进行捕获

([A-Z][a-z]*(?: [A-Z][a-z]*)*)

Demo

【讨论】: