是否可以简化这些Python正则表达式?
patterns = {}
patterns[1] = re.compile("[A-Z]\d-[A-Z]\d")
patterns[2] = re.compile("[A-Z]\d-[A-Z]\d\d")
patterns[3] = re.compile("[A-Z]\d\d-[A-Z]\d\d")
patterns[4] = re.compile("[A-Z]\d\d-[A-Z]\d\d\d")
patterns[5] = re.compile("[A-Z]\d\d\d-[A-Z]\d\d\d")
patterns[6] = re.compile("[A-Z][A-Z]\d-[A-Z][A-Z]\d")
patterns[7] = re.compile("[A-Z][A-Z]\d-[A-Z][A-Z]\d\d")
patterns[8] = re.compile("[A-Z][A-Z]\d\d-[A-Z][A-Z]\d\d")
patterns[9] = re.compile("[A-Z][A-Z]\d\d-[A-Z][A-Z]\d\d\d")
patterns[10] = re.compile("[A-Z][A-Z]\d\d\d-[A-Z][A-Z]\d\d\d")
def matchFound(toSearch):
for items in sorted(patterns.keys(), reverse=True):
matchObject = patterns[items].search(toSearch)
if matchObject:
return items
return 0
然后我用以下代码来查找匹配项:
while matchFound(toSearch) > 0:
我有10个不同的正则表达式,但我觉得它们可以用一个写得更好、更优雅的正则表达式来替代。你们觉得有可能吗?
编辑:我忘了两个表达式:
patterns[11] = re.compile("[A-Z]\d-[A-Z]\d\d\d")
patterns[12] = re.compile("[A-Z][A-Z]\d-[A-Z][A-Z]\d\d\d")
编辑2:我最后得到了以下结果。我意识到我可能会得到额外的结果,但我认为在我解析的数据中不太可能出现。
patterns = {}
patterns[1] = re.compile("[A-Z]{1,2}\d-[A-Z]{1,2}\d{1,3}")
patterns[2] = re.compile("[A-Z]{1,2}\d\d-[A-Z]{1,2}\d{2,3}")
patterns[3] = re.compile("[A-Z]{1,2}\d\d\d-[A-Z]{1,2}\d\d\d")
4 个回答
Josh说得对,至少应该减少正则表达式的数量。
不过,你也可以使用一个范围更大的正则表达式,然后再额外检查所有条件是否都满足。比如说:
pattern = re.compile("([A-Z]{1,2})(\d{1,3})-([A-Z]{1,2})(\d{1,3})")
然后再进行:
matchObject = pattern.search(toSearch)
if matchObject and <do something with the length of the groups, comparing them)>:
return <stuff>
但即使这样做因为某些原因不奏效,还有其他方法可以改进:
patterns = tuple(re.compile(r) for r in (
"[A-Z]\d-[A-Z]\d{1,2}",
"[A-Z]\d\d-[A-Z]\d{2,3}",
"[A-Z]\d\d\d-[A-Z]\d\d\d",
"[A-Z][A-Z]\d-[A-Z][A-Z]\d{1,2}",
"[A-Z][A-Z]\d\d-[A-Z][A-Z]\d{2,3}",
"[A-Z][A-Z]\d\d\d-[A-Z][A-Z]\d\d\d",
)
def matchFound(toSearch):
for pat in reversed(patterns):
matchObject = pat.search(toSearch)
if matchObject:
return items # maybe more useful?
return None
Sean Bright给了你需要的答案。这里有个一般性的建议:
Python有很棒的文档。在这种情况下,你可以使用“help”命令来查看:
import re
help(re)
如果你仔细阅读帮助内容,你会看到:
{m,n} Matches from m to n repetitions of the preceding RE.
使用谷歌也很有帮助。搜索“Python 正则表达式”能找到这些链接:
http://docs.python.org/library/re.html
http://docs.python.org/howto/regex.html
这两个链接都值得一读。
Josh Caswell提到,Sean Bright的回答能匹配更多的输入,而不是你最初的那种。抱歉我没有早点发现这个问题。(将来可能需要更详细地描述一下你的问题。)
所以你面临的基本问题是,正则表达式不能进行计数。不过,我们可以在Python中用一种很简洁的方式来解决这个问题。首先,我们要创建一个模式,这个模式可以匹配你所有合法的输入,但也会匹配一些你想要拒绝的输入。
接下来,我们定义一个函数,利用这个模式,然后检查匹配的结果,并进行计数,以确保匹配的字符串符合长度要求。
import re
_s_pat = r'([A-Z]{1,2})(\d{1,3})-([A-Z]{1,2})(\d{1,3})'
_pat = re.compile(_s_pat)
_valid_n_len = set([(1,1), (1,2), (1,3), (2,2), (2,3), (3,3)])
def check_match(s):
m = _pat.search(s)
try:
a0, n0, a1, n1 = m.groups()
if len(a0) != len(a1):
return False
if not (len(n0), len(n1)) in _valid_n_len:
return False
return True
except (AttributeError, TypeError, ValueError):
return False
下面是对上述代码的一些解释。
首先,我们使用原始字符串来定义模式,然后预编译这个模式。我们可以直接把字符串放到re.compile()
的调用中,但我喜欢把它单独放在一个变量里。我们的模式有四个不同的部分,用括号括起来;这些部分会变成“匹配组”。有两个匹配组用来匹配字母字符,还有两个匹配组用来匹配数字。这个模式可以匹配你想要的所有内容,但不会排除一些你不想要的内容。
接下来,我们声明一个set
,里面包含所有有效的数字长度。例如,第一组数字可以是1位,第二组可以是2位;这就是(1,2)
(一个tuple
值)。使用集合是一种很好的方式来指定我们想要的所有合法组合,同时还能快速检查给定的长度对是否合法。
函数check_match()
首先使用模式去匹配字符串,返回一个“匹配对象”,并将其绑定到名称m
上。如果匹配失败,m
可能会被设置为None
。我没有直接测试None
,而是用了一个try
/except
块;回想起来,直接测试None
可能更好。抱歉,我不是故意让人困惑的。不过,try
/except
块是一种很简单的方式来处理事情,让它变得很可靠,所以我经常用它来处理类似的情况。
最后,check_match()
将匹配组解包成四个变量。两个字母组是a0和a1,两个数字组是n0和n1。然后它检查这些长度是否合法。根据我的理解,规则是字母组需要长度相同;接着我们构建一个数字组长度的tuple
,并检查这个tuple
是否在我们有效的set
中。
这里有一个稍微不同的版本,也许你会更喜欢这个。
import re
# match alpha: 1 or 2 capital letters
_s_pat_a = r'[A-Z]{1,2}'
# match number: 1-3 digits
_s_pat_n = r'\d{1,3}'
# pattern: four match groups: alpha, number, alpha, number
_s_pat = '(%s)(%s)-(%s)(%s)' % (_s_pat_a, _s_pat_n, _s_pat_a, _s_pat_n)
_pat = re.compile(_s_pat)
# set of valid lengths of number groups
_valid_n_len = set([(1,1), (1,2), (1,3), (2,2), (2,3), (3,3)])
def check_match(s):
m = _pat.search(s)
if not m:
return False
a0, n0, a1, n1 = m.groups()
if len(a0) != len(a1):
return False
tup = (len(n0), len(n1)) # make tuple of actual lengths
if not tup in _valid_n_len:
return False
return True
注意:看起来有效长度的规则其实很简单:
if len(n0) > len(n1):
return False
如果这个规则对你有效,你可以去掉集合和元组的部分。嗯,我还会把变量名缩短一点。
import re
# match alpha: 1 or 2 capital letters
pa = r'[A-Z]{1,2}'
# match number: 1-3 digits
pn = r'\d{1,3}'
# pattern: four match groups: alpha, number, alpha, number
p = '(%s)(%s)-(%s)(%s)' % (pa, pn, pa, pn)
_pat = re.compile(p)
def check_match(s):
m = _pat.search(s)
if not m:
return False
a0, n0, a1, n1 = m.groups()
if len(a0) != len(a1):
return False
if len(n0) > len(n1):
return False
return True