【问题标题】:Python equivallent of Perl match and capture within if block在 if 块中与 Perl 匹配和捕获的 Python 等价物
【发布时间】:2015-04-03 09:41:59
【问题描述】:

我正在慢慢地从 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 的座右铭是“应该有一种——最好只有一种——明显的方式来做到这一点”——关于这里最好的方式是什么的任何想法/建议?

提前感谢您的任何建议。

最好的问候, -帕维尔

【问题讨论】:

  • 你不......你不能做有条件的作业
  • 将您的正则表达式定义为原始字符串。
  • @JoranBeasley - 我试图弄清楚在 Python 中执行此操作的最佳方法是什么,而我的代码中没有效率低下。
  • @AvinashRaj - 抱歉,不清楚这有什么帮助?
  • 对于那些使用 Python 3.8 的人来说仅供参考:赋值表达式是可能的,请参阅 stackoverflow.com/questions/122277/…

标签: python regex


【解决方案1】:

关于您的代码的几点说明:

  1. 预编译的正则表达式
    如果您不打算重用正则表达式,则无需显式编译它们。通过使用模块级函数,您可以获得更简洁的代码:
    使用m = re.match(pattern, text)
    而不是p1 = re.compile(pattern),后跟m = p1.match(str)

  2. 尝试匹配,如果匹配 - 使用匹配组格式化输出
    Python 正则表达式工具提供了一个非常适合您的情况的函数:re.subn()。 它执行正则表达式替换并返回许多替换。

  3. 性能注意事项

    • re.match() 调用了两次 - 它将尝试匹配同一行两次并返回两个不同的匹配对象。这可能会花费您一些额外的周期。
    • re.compile()(或模块级匹配函数)调用了两次 - 根据docs可以:

      注意:传递给 re.compile() 的最新模式的编译版本和模块级匹配函数被缓存,因此一次只使用几个正则表达式的程序不必担心编译正则表达式.

    • 如何避免正则表达式预编译
      该代码定义了匹配输入字符串时应遵循的正则表达式的顺序。只有当我们 100% 确定我们需要它时,编译正则表达式才有意义。请参阅下面的代码。这比实际的解释要简单得多。
    • 过早的优化
      您没有遇到任何性能问题,不是吗?尽早优化这一点,您可能会花费一些时间而没有任何明显的影响。

座右铭:

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')

希望对你有所帮助

【讨论】:

    【解决方案2】:

    如果您绝对决定不执行两次相同的正则表达式匹配,您可以这样做:

    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)
    

    我个人认为这种级别的微优化不会有太大的不同, 但是有办法完成它。

    【讨论】:

    • 嗨,Marius - 只是为了确认一下 - 你是说我的代码很好,而且你会非常相似,完全不用担心匹配两次?谢谢,
    • 是的,基本上。不过,您可能不应该使用Exception,而是提出更具体的内容,例如ValueError
    猜你喜欢
    • 2018-01-02
    • 2021-02-17
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 2010-10-16
    • 2020-01-24
    相关资源
    最近更新 更多