6

我必须从没有共同逻辑的文件名中解析一些数字。我想使用python的“尝试,你会被原谅”的方式,或者try-except结构。现在我必须添加两个以上的案例。这样做的正确方法是什么?我现在正在考虑嵌套尝试或尝试除通,尝试除通,...哪个更好还是别的什么?工厂方法也许(如何?)?

这必须在未来很容易扩展,因为会有更多的案例。

以下是我想要的(不起作用,因为每次尝试只能存在一个例外):

try:
    # first try
    imNo = int(imBN.split('S0001')[-1].replace('.tif',''))
except:
    # second try
    imNo = int(imBN.split('S0001')[-1].replace('.tiff',''))
except:
    # final try
    imNo = int(imBN.split('_0_')[-1].replace('.tif',''))

编辑

哇,感谢您的回答,但请不要进行模式匹配。我的错,把“一些共同的逻辑”放在开头(现在改为“没有共同的逻辑”,对此感到抱歉)。在上述情况下,模式非常相似......让我添加一些完全不同的东西来说明这一点。

except:
    if imBN.find('first') > 0: imNo = 1
    if imBN.find('second') > 0: imNo = 2
    if imBN.find('third') > 0: imNo = 3
    ...
4

2 回答 2

10

您可以提取通用结构并列出可能的参数:

tries = [
    ('S0001', '.tif'),
    ('S0001', '.tiff'),
    ('_0_', '.tif'),
]

for sep, subst in tries:
    num = imBN.split(sep)[-1].replace(subst, '')
    try:
        imNo = int(num)
        break
    except ValueError:
        pass
else:
    raise ValueError, "String doesn't match any of the possible patterns"

更新以回应问题编辑

通过使用 lambda,这种技术可以很容易地适应任意表达式:

def custom_func(imBN):
    if 'first' in imBN: return 1
    if 'second' in imBN: return 2

tries = [
    lambda: int(imBN.split('S0001')[-1].replace('.tif','')),
    lambda: int(imBN.split('S0001')[-1].replace('.tiff','')),
    lambda: int(imBN.split('_0_')[-1].replace('.tif','')),
    lambda: custom_func(imBN),
]

for expr in tries:
    try:
        result = expr()
        break
    except:
        pass
else:
    # error
于 2012-04-25T14:36:03.503 回答
3

在您的特定情况下,正则表达式将无需执行这些 try-except 块。这样的事情可能会抓住你的情况:

>>> import re
>>> re.match('.*(S0001|_0_)([0-9]+)\..*$', 'something_0_1234.tiff').groups()
('_0_', '1234')
>>> re.match('.*(S0001|_0_)([0-9]+)\..*$', 'somethingS00011234.tif').groups()
('S0001', '1234')
>>> re.match('.*(S0001|_0_)([0-9]+)\..*$', 'somethingS00011234.tiff').groups()
('S0001', '1234')

对于您关于串行 try-except 块的问题,Niklas B. 的答案显然是一个很好的答案。

编辑: 您正在做的事情称为模式匹配,那么为什么不使用模式匹配库呢?如果正则表达式字符串困扰您,有更简洁的方法可以做到:

import re
matchers = []
sep = ['S0001', '_0_']
matchers.append(re.compile('^.*(' + '|'.join(sep) + ')(\d+)\..*$'))
matchers.append(some_other_regex_for_other_cases)

for matcher in matchers:
    match = matcher.match(yourstring)
    if match:
        print match.groups()[-1]

另一种更通用的方式与自定义函数兼容:

import re
matchers = []
simple_sep = ['S0001', '_0_']
simple_re = re.compile('^.*(' + '|'.join(sep) + ')(\d+)\..*$')
def simple_matcher(s):
    m = simple_re.match(s)
    if m:
        return m.groups()[-1]

def other_matcher(s):
    if s[3:].isdigit():
        return s[3:]

matchers.append(simple_matcher)
matchers.append(other_matcher)

for matcher in matchers:
    match = matcher('yourstring')
    if match:
        print int(match)
于 2012-04-25T14:55:11.903 回答