Python 正则表达式:捕获包含空格的多个字符串部分
我正在尝试从一个字符串中提取子字符串,这个字符串看起来像这样:
'some string, another string, '
我希望得到的结果匹配组是:
('some string', 'another string')
我现在的解决方案
>>> from re import match
>>> match(2 * '(.*?), ', 'some string, another string, ').groups()
('some string', 'another string')
可以工作,但不太实用——我在这里展示的内容相比于我在实际项目中做的要简单得多;我想只使用一个“直接的”(非计算的)正则表达式模式。不幸的是,我到目前为止的尝试都失败了:
这个匹配不成功(结果为None),因为{2}只应用于空格,而不是整个字符串:
>>> match('.*?, {2}', 'some string, another string, ')
在重复的字符串周围加上括号会导致结果中包含逗号和空格
>>> match('(.*?, ){2}', 'some string, another string, ').groups()
('another string, ',)
再加一组括号虽然解决了这个问题,但结果却太多了:
>>> match('((.*?), ){2}', 'some string, another string, ').groups()
('another string, ', 'another string')
添加一个非捕获修饰符改善了结果,但仍然漏掉了第一个字符串
>>> match('(?:(.*?), ){2}', 'some string, another string, ').groups()
('another string',)
我觉得我快找到了,但似乎还是找不到合适的方法。
有人能帮我吗?有没有其他我没想到的方法?
在收到前几条回复后的更新:
首先,非常感谢大家,你们的帮助让我非常感激!:-)
正如我在原帖中所说,我为了描述实际的核心问题,省略了很多复杂的内容。首先,在我正在进行的项目中,我正在解析大量文件(目前每天数以万计),这些文件有多种(目前5种,未来可能达到25种,甚至几百种)基于行的格式。还有XML、JSON、二进制和其他一些数据文件格式,但我们还是专注于这个吧。
为了应对多种文件格式,并利用其中许多是基于行的事实,我创建了一个相对通用的Python模块,它会一个接一个地加载文件,对每一行应用正则表达式,并返回一个包含匹配结果的大数据结构。这个模块是一个原型,生产版本将需要一个C++版本以提高性能,并将通过Boost::Python连接,可能还会增加正则表达式方言的复杂性。
另外,重复的次数并不是2,而是目前在0到70之间变化,逗号也不总是逗号,尽管我最初说过,正则表达式模式的某些部分必须在运行时计算;可以说,我有理由尝试减少“动态”数量,尽可能多地使用“固定”模式。
所以,总的来说:我必须使用正则表达式。
尝试重新表述:我认为问题的核心在于:是否有一种Python正则表达式表示法,例如涉及大括号重复,并允许我捕获
'some string, another string, '
到
('some string', 'another string')
?
嗯,这可能缩小得太多了——但无论你怎么做都是错的 :-D
第二次尝试重新表述:为什么我在结果中看不到第一个字符串('some string')?为什么正则表达式会产生匹配(表明必须有两个某种东西),但只返回一个字符串(第二个)?
即使我使用非数字重复,比如用+代替{2},问题依然存在:
>>> match('(?:(.*?), )+', 'some string, another string, ').groups()
('another string',)
而且,返回的不是第二个字符串,而是最后一个:
>>> match('(?:(.*?), )+', 'some string, another string, third string, ').groups()
('third string',)
再次感谢大家的帮助,真让我惊讶的是,同行评审在我试图找出我真正想知道的事情时是多么有帮助……
6 个回答
如上所述,我认为这个正则表达式工作得很好:
import re
thepattern = re.compile("(.+?)(?:,|$)") # lazy non-empty match
thepattern.findall("a, b, asdf, d") # until comma or end of line
# Result:
Out[19]: ['a', ' b', ' asdf', ' d']
这里的关键是要使用 findall
,而不是 match
。你提问的方式让人觉得你更喜欢用 match
,但这并不是解决这个问题的正确工具——它的设计是为了每个正则表达式中的对应组 (
)
返回一个确切的字符串。因为你的“字符串数量”是变化的,所以正确的方法是使用 findall
或 split
。
如果这不是你需要的,请把问题描述得更具体一些。
编辑:如果你必须使用元组而不是列表:
tuple(Out[19])
# Result
Out[20]: ('a', ' b', ' asdf', ' d')
除非这个问题比你解释的要复杂得多,否则我觉得用正则表达式没什么必要。其实用基本的字符串方法来处理这个问题是非常简单的:
[s.strip() for s in mys.split(',') if s.strip()]
如果必须用元组的话:
tuple(s.strip() for s in mys.split(',') if s.strip())
这样写的代码也更容易理解。如果你觉得这样不适用,请告诉我。
编辑:好的,确实这个问题比最开始看起来的要复杂。不过我还是把这个留着作为历史记录。(看来我不是很“守规矩” :))
总结一下,我似乎已经在用最好的方法,通过一种“动态”的方式来构建正则表达式模式:
>>> from re import match
>>> match(2 * '(.*?), ', 'some string, another string, ').groups()
('some string', 'another string')
这个
2 * '(.*?)
就是我所说的动态。另一种方法
>>> match('(?:(.*?), ){2}', 'some string, another string, ').groups()
('another string',)
没有得到想要的结果,因为(正如Glenn和Alan友好地解释的那样)
在匹配时,捕获的内容会在每次重复捕获组时被覆盖
感谢大家的帮助!:-)