将捕获的组放在一行中

问题描述 投票:11回答:9

有一个已知的“模式”来获取捕获的组值,如果不匹配则有一个空字符串:

match = re.search('regex', 'text')
if match:
    value = match.group(1)
else:
    value = ""

要么:

match = re.search('regex', 'text')
value = match.group(1) if match else ''

有一种简单而pythonic的方法可以在一行中完成吗?

换句话说,如果没有找到捕获组,我可以提供默认值吗?


例如,我需要从_字符串后面的文本中提取所有字母数字字符(和key=):

>>> import re
>>> PATTERN = re.compile('key=(\w+)')
>>> def find_text(text):
...     match = PATTERN.search(text)
...     return match.group(1) if match else ''
... 
>>> find_text('foo=bar,key=value,beer=pub')
'value'
>>> find_text('no match here')
''

find_text()有可能成为一个单行班吗?

这只是一个例子,我正在寻找一种通用的方法。

python regex syntax
9个回答
10
投票

引自MatchObjects docs

匹配对象的布尔值始终为True。由于match()search()在没有匹配时返回None,您可以测试是否与简单的if语句匹配:

match = re.search(pattern, string)
if match:
   process(match)

由于没有其他选项,并且当您使用函数时,我想提供此替代方案

def find_text(text, matches = lambda x: x.group(1) if x else ''):
    return matches(PATTERN.search(text))

assert find_text('foo=bar,key=value,beer=pub') == 'value'
assert find_text('no match here') == ''

它是完全相同的,但只有你需要做的检查是默认参数化。

考虑到@ Kevin的解决方案和@ devnull在评论中的建议,你可以做这样的事情

def find_text(text):
    return next((item.group(1) for item in PATTERN.finditer(text)), "")

这利用了以下事实:next接受默认值作为参数返回。但这有在每次迭代时创建生成器表达式的开销。所以,我会坚持第一个版本。


4
投票

您可以使用捕获组中字符串末尾的空替代项来使用该模式:

>>> re.search(r'((?<=key=)\w+|$)', 'foo=bar,key=value').group(1)
'value'
>>> re.search(r'((?<=key=)\w+|$)', 'no match here').group(1)
''

3
投票

可以在单个单行中引用函数调用的结果两次:创建一个lambda表达式并在参数中调用该函数。

value = (lambda match: match.group(1) if match else '')(re.search(regex,text))

但是,我不认为这特别易读。负责任地编码 - 如果您要撰写棘手的代码,请留下描述性评论!


3
投票

回复:“在一条线上有没有简单的pythonic方式?”答案是不。任何让它在一行中工作的方法(没有定义你自己的包装器),阅读比你已经提出的方式更难听。但是定义自己的包装器完全是Pythonic,因为使用两条非常易读的线而不是一条难以读取的线。


2
投票

单行版:

if re.findall(pattern,string): pass

这里的问题是你想要准备多个匹配或确保你的模式只打一次。扩展版本:

# matches is a list
matches = re.findall(pattern,string)

# condition on the list fails when list is empty
if matches:
    pass

因此,对于您的示例“在key = string”之后从文本中提取所有字母数字字符(和_):

# Returns 
def find_text(text):
    return re.findall("(?<=key=)[a-zA-Z0-9_]*",text)[0]

2
投票

一个衬里,一个衬里......为什么你不能在2行上写它?

getattr(re.search('regex', 'text'), 'group', lambda x: '')(1)

如果没问题,你的第二个解如果您愿意,可以从中创建一个功能。我的解决方案是出于演示目的,它绝不是pythonic。


2
投票

一条线给你,虽然不是Pythonic。

find_text = lambda text: (lambda m: m and m.group(1) or '')(PATTERN.search(text))

实际上,在Scheme编程语言中,所有局部变量构造都可以从lambda函数应用程序派生。


1
投票

你可以这样做:

value = re.search('regex', 'text').group(1) if re.search('regex', 'text') else ''

考虑到你运行正则表达式两次这一事实,它并不是非常有效。

或者@Kevin建议只运行一次:

value = (lambda match: match.group(1) if match else '')(re.search(regex,text))


1
投票

启动Python 3.8,并引入assignment expressions (PEP 572):=运算符),我们可以命名正则表达式搜索表达式pattern.search(text),以便检查是否存在匹配(因为pattern.search(text)返回Nonere.Match对象)并使用它来提取匹配组:

# pattern = re.compile(r'key=(\w+)')
match.group(1) if (match := pattern.search('foo=bar,key=value,beer=pub')) else ''
# 'value'
match.group(1) if (match := pattern.search('no match here')) else ''
# ''
© www.soinside.com 2019 - 2024. All rights reserved.