我正在慢慢地从Perl迁移到Python,并试图了解使用正则表达式的最佳实践。
我有以下Perl代码 - 这段代码基本上以字符串作为输入,并根据正则表达式匹配和捕获将重新排列的字符串作为输出吐出:
#!/usr/bin/env perl
use strict;
use warnings;
my $str = $ARGV[0] || die "Arg?";
my $result;
if($str =~ m/^\d{12}$/) {
$result = $str;
} elsif($str =~ m{^(\d{2})/(\d{2})/(\d{4})$}) {
$result = "${1}${2}0000${3}";
} elsif($str =~ m{^(\d{4})$}) {
$result = "01010000${1}";
} else {
die "Invalid string";
}
print("Result: $result\n");
Python 3中有什么好处?
到目前为止,我提出了以下内容,但在elif部分匹配两次似乎效率低下。在开始时编译所有正则表达式也似乎效率低下。
#!/usr/bin/env python3
import re, sys
str = sys.argv[1]
p1 = re.compile('\d{12}')
p2 = re.compile('(\d{2})/(\d{2})/(\d{4})')
p3 = re.compile('(\d{4})')
if p1.match(str):
result = str
elif p2.match(str):
m = p2.match(str)
result = '%s%s0000%s' % (m.group(1), m.group(2), m.group(3))
elif p3.match(str):
m = p3.match(str)
result = '01010000%s' % (m.group(1))
else:
raise Exception('Invalid string')
print('Result: ' + result)
鉴于Python的座右铭"应该有一个 - 最好只有一个 - 明显的方式去做" - 关于这里最好的方法是什么的任何想法/建议?
提前感谢您提出任何建议。
祝你好运, -Pavel
答案 0 :(得分:2)
关于您的代码的几点说明:
预编译的正则表达式
如果您不打算重用它们,则无需显式编译正则表达式。通过使用模块级功能,您可以获得更清晰的代码:
使用m = re.match(pattern, text)
而不是p1 = re.compile(pattern)
后跟m = p1.match(str)
尝试匹配(如果匹配) - 使用匹配的组格式输出
Python正则表达式工具提供了一个完全适合您案例的函数:re.subn()
。
它执行正则表达式替换并返回许多替换。
效果考虑因素
re.match()
被调用两次 - 它将尝试匹配同一行两次并返回两个不同的匹配对象。它可能会花费你一些额外的周期。 re.compile()
(或模块级匹配函数)调用两次 -
根据{{3}}:,这没关系
注意:传递给re.compile()的最新模式的编译版本和模块级匹配函数被缓存,因此一次只使用几个正则表达式的程序不必担心编译正则表达式
座右铭:
import re
rules = ( (r'\d{12}', r'\g<0>')
, (r'(\d{2})/(\d{2})/(\d{4})', r'\1\g<2>0000\3')
#using r'\1\20000\3' would imply group 1 followed by group 20000!
, (r'(\d{4})', r'01010000\1') )
def transform(text):
for regex, repl in rules:
# we're compiling only those regexes we really need
result, n = re.subn(regex, repl, text)
if n: return result
raise ValueError('Invalid string')
tests = ['1234', r'12/34/5678', '123456789012']
for test in tests:
print(transform(test))
transform('this line supposed to trigger exception')
希望这有帮助
答案 1 :(得分:1)
如果您绝对决定不再执行相同的正则表达式匹配,则可以执行以下操作:
p1 = re.compile('\d{12}')
p2 = re.compile('(\d{2})/(\d{2})/(\d{4})')
p3 = re.compile('(\d{4})')
# Functions to perform the processing steps required for each
# match- might be able to save some lines of code by making
# these lambdas
def result1(s, m):
return s
def result2(s, m):
return '%s%s0000%s' % (m.group(1), m.group(2), m.group(3))
def result3(s, m):
return '01010000%s' % (m.group(1))
for pattern, result_getter in [(p1, result1), (p2, result2), (p3, result3)]:
m = pattern.match(str)
if m:
result = result_getter(str, m)
break
print('Result: ' + result)
我个人认为这种微观优化水平不会产生太大影响, 但有一种方法可以完成它。