查找不匹配给定正则表达式的字符
我正在写一个程序,用来验证和修正一个给定的日期字符串。比如说,我们有一个日期 04121987
,它的格式是 ddmmyyyy
。为了处理这样的日期,我们可以用一个正则表达式。
(0[1-9]|[12][0-9]|3[01])(0[1-9]|1[012])(19\d\d|20\d\d)
当我用正则表达式去匹配我的字符串时,效果很好。在Python中:
>>> regex = re.compile(r'(0[1-9]|[12][0-9]|3[01])(0[1-9]|1[012])(19\d\d|20\d\d)')
>>> regex.findall('04121987')
[('04', '12', '1987')]
如果我有一个字符串 04721987
,我们可以明显看到 72
不是一个有效的月份,所以这个字符串就不会和正则表达式匹配。
>>> regex.findall('04721987')
[]
我想要找出导致正则表达式匹配失败的字符以及它的位置。在这个例子中,就是 7
。我该如何在Python中做到这一点呢?
4 个回答
这个解决方案非常复杂,我希望你能找到更好的方法。这个代码经过简单测试,可能已经足够用了。errorindex() 这个函数接收一个日期字符串,然后返回一个错误条目的索引列表。不过,如果第一个月份的数字不正确,就会出现一些模糊的情况。在不知道第一个数字的情况下,无法判断第二个数字是否正确。下面是代码。注意:我忘记考虑闰年了!
def errorindex(s):
err = []
for i in range(len(s)):
if i == 0: #month1
if int(s[i]) < 0 or int(s[i]) > 1:
err.append(i)
if i == 1: #month2
if int(s[i-1]) == 0:
if int(s[i]) < 1 or int(s[i]) > 9:
err.append(i)
elif int(s[i-1]) == 1:
if int(s[i]) < 0 or int(s[i]) > 2:
err.append(i)
else:
if int(s[i]) < 0 or int(s[i]) > 2:
err.append(i)
if i == 2: #day1
if int(s[i]) < 0 or int(s[i]) > 3:
err.append(i)
if i == 3: #day2
if int(s[i-1]) in [0,1,2] and str(s[:2]) != '02':
if int(s[i]) < 0 or int(s[i]) > 9:
err.append(i)
elif int(s[i-1]) in [0,1,2] and str(s[:2]) == '02':
if int(s[i]) < 0 or int(s[i]) > 8:
err.append(i)
if i == 4: #year1
if int(s[i]) < 1 or int(s[i]) > 2:
err.append(i)
if i == 5: #year2
if int(s[i-1]) == 1:
if int(s[i]) != 9:
err.append(i)
elif int(s[i-1]) == 2:
if int(s[i]) != 0:
err.append(i)
if i ==6:
if int(s[i]) < 0 or int(s[i]) > 9:
err.append(i)
if i ==7:
if int(s[i]) < 0 or int(s[i]) > 9:
err.append(i)
return err
s = '04721987'
print(errorindex(s))
一种可能的方法是构建一个正则表达式,它可以匹配任何内容,但会把好的匹配和坏的匹配放在不同的组里。通过检查结果中哪些组被填充,可以知道哪个组失败了。
>>> regex = re.compile(r'(?:(0[1-9]|[12][0-9]|3[01])|(.{,2}))(?:(0[1-9]|1[012])|(.{,2}))(?:(19\d\d|20\d\d)|(.{,4}))')
>>> regex.match('04121987').groups()
('04', None, '12', None, '1987', None)
>>> regex.match('04721987').groups()
('04', None, None, '72', '1987', None)
>>> regex.match('0412').groups()
('04', None, '12', None, None, '')
另一种方法是选择一个合适的有效字符串作为基础,然后逐个字符地用输入字符串替换它,并在每次替换后进行验证。在这里,我使用了 datetime.datetime.strptime
来进行验证。你也可以使用正则表达式,不过它需要能够接受到2999年的年份,所以问题中的那个正则表达式是行不通的。
from datetime import datetime
def str_to_date(s):
good_date = '01011999'
for i in xrange(len(good_date)):
try:
d = datetime.strptime(s[:i+1] + good_date[i+1:], '%d%m%Y')
except ValueError:
raise ValueError("Bad character '%s' at index %d" % (s[i:i+1], i))
return d
我觉得你想要的事情可能做不到,因为 _sre
模块是用 C 语言写的 ;(。
你可以试试用 这个包,通过修改 sre_compile
的路径,先导入你新的 _sre
等等,但我觉得这样做不太值得。这个包是完全用 Python 写的 _sre
实现,所以你能看到源代码,修改它,并在下一个字符不匹配时做点什么。
你也可以通过以下方式做类似的事情:
- 把日期字符串分成三部分(天、月和年),然后分别匹配正则表达式
- 用其他不涉及正则表达式的方法来验证日期时间
也许你不能准确找到错误的数字,但在这种情况下,我觉得这没什么太大意义,只要你能告诉用户哪里出错了(是天、月还是年)。