我正在将一个解析工具从 Perl 移植到 Python:
my $lineno = 1;
my @data;
for my $line (split /\R/, $source) {
$line =~ s/^([ ]*)//;
my $indent = length $1;
push @data, [$lineno++, $indent, $line];
}
这个
- 使用 Unicode 行分隔符将输入拆分成行,
- 去除前导空格(仅 U+0020 空格字符),
- 确定剥离空间的缩进级别。
我发现很难将其转换为惯用的 Python,因为 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 解决方案是什么? 我找不到“唯一一个明显的方法来做到这一点”。 也许我错过了一个特定的习语?
请您参考如下方法:
我非常怀疑您是否会找到任何方法在 Python 中执行与在 Perl 中一样自然的正则表达式。正则表达式是 Perl 设计的非常低的一部分,但它们几乎不是 Python 的核心。
所以我的第一个建议是考虑是否可以避免一起使用正则表达式。对于您的示例问题,这很容易,只需使用 line.lstrip(' ') 并比较长度以确定删除了多少缩进。也许您会考虑的其他一些问题也可以使用字符串方法而不是正则表达式轻松实现。
我真的怀疑是否存在任何比您考虑过的所有选项都好得多的通用正则表达式替换解决方案。我自己可能会使用类似您的尝试 2 的方法,或者尝试 1,其中缩进量由内部函数保存,而不是匹配对象本身。
