Python 如何执行单个替换,然后使用正则表达式捕获?
我正在将一个解析工具从Perl移植到Python:Python 如何执行单个替换,然后使用正则表达式捕获?,python,regex,Python,Regex,我正在将一个解析工具从Perl移植到Python: my$lineno=1; 我的@数据; 对于我的$行(拆分/\R/,$source){ $line=~s/^([]*)/; my$indent=长度$1; 推送@data,[$lineno++,$indent,$line]; } 这个 使用Unicode行分隔符将输入拆分为行 带前导空格(仅U+0020空格字符) 根据剥离空间确定索引级别 我发现很难将其转换为惯用Python,因为re.sub()只返回替换后的字符串,而不返回匹配对象(我
my$lineno=1;
我的@数据;
对于我的$行(拆分/\R/,$source){
$line=~s/^([]*)/;
my$indent=长度$1;
推送@data,[$lineno++,$indent,$line];
}
这个
- 使用Unicode行分隔符将输入拆分为行
- 带前导空格(仅U+0020空格字符)
- 根据剥离空间确定索引级别
re.sub()
只返回替换后的字符串,而不返回匹配对象(我需要计算删除的空格)
在这个特定的示例中,我可以简单地比较替换前后字符串的长度。
但我对这类问题的一般解决方案感兴趣:
如何在访问正则表达式捕获的同时执行单个替换?
尝试1–通过替换函数过滤匹配对象:
lineno = 1
data = []
re_leading_space = re.compile(r'^([ ]*)')
for line in source.splitlines(): # TODO handle Unicode line seps
m = None
def exfiltrate(the_match):
nonlocal m
m = the_match
return ''
line = re_leading_space.sub(exfiltrate, line, count=1)
indent = len(m.group(1)) if m is not None else 0
data.append((lineno, indent, line))
lineno += 1
缺点:怪异的非本地的数据流
尝试2–手动执行替换:
lineno = 1
data = []
re_leading_space = re.compile(r'^([ ]*)')
for line in source.splitlines(): # TODO handle Unicode line seps
m = re_leading_space.match(line)
indent = 0
if m is not None:
line = line[m.end():] # remove matched prefix
indent = len(m.group(1))
data.append((lineno, indent, line))
lineno += 1
缺点:虽然在其他方面相当清楚,但它最终只是标准库的一个糟糕的重新实现
尝试3–执行匹配,然后再次匹配正则表达式作为替换:
lineno = 1
data = []
re_leading_space = re.compile(r'^([ ]*)')
for line in source.splitlines(): # TODO handle Unicode line seps
m = re_leading_space.match(line)
line = re_leading_space.sub('', line, count=1)
indent = len(m.group(1)) if m is not None else 0
data.append((lineno, indent, line))
lineno += 1
缺点:虽然相对简洁,但这不需要两次匹配模式。必须注意为match()
和sub()
提供相同的标志等
那么,这个问题的Pythonic解决方案是什么呢?
我找不到“一个也是唯一一个显而易见的方法。”
也许我遗漏了一个特定的习惯用法?有一个扩展
方法,它被记录为:
返回通过对
模板字符串模板,由sub()方法完成。逃逸,如
\n转换为适当的字符和数字
反向引用(\1、\2)和命名反向引用(\g、\g)是
替换为相应组的内容
这只允许匹配一次并使用匹配进行替换,如下所示:
data = []
re_leading_space = re.compile(r'^([ ]*)(.*)')
for lineno, line in enumerate(source.splitlines()): # TODO handle Unicode line seps
m = re_leading_space.match(line)
indent = 0
if m is not None:
line = m.expand(r'\2')
indent = len(m.group(1))
data.append((lineno, indent, line))
我强烈怀疑您是否能找到任何方法在Python中实现像在Perl中一样自然的正则表达式。正则表达式是Perl设计中非常低级的一部分,而它们在Python中并不那么重要
我的第一个建议是考虑是否可以避免使用正则表达式。对于简单的示例问题,只需使用line.lstrip(“”)
并比较长度来计算删除了多少缩进。也许你会考虑的其他问题也会使用字符串方法,而不是正则表达式。
我真的怀疑有没有比你考虑过的所有选项都更好的通用正则表达式替换的解决方案。我可能会使用类似于您自己的尝试2,或者尝试1,其中缩进量是由内部函数保存的,而不是匹配对象本身。我看不出这与问题有什么关系。您打算如何使用expand
方法?我添加了一个如何使用它的示例,因为显然文档还不够。这实际上有些帮助,因为它可以让我在一般情况下正确地实现尝试2。例如,def-substitute\u-match(match,orig,template):返回orig[:match.start()]+match.expand(template)+orig[match.end():]
。我不会为不匹配的字符串添加另一个捕获组(这对于regex.match(…,pos=n)
)。m.expand(r'\2')
与m.group(2)
一样。在这里一点用都没有。如果您需要访问组的内容,请使用group
,而不是expand
。您是正确的,但我试图回答原始问题的这一部分:如何在访问正则表达式捕获的同时执行单个替换?嗯,好的,谢谢您再次确认没有更好的方法。我最终选择了尝试2的变体。