查找不匹配给定正则表达式的字符

3 投票
4 回答
2501 浏览
提问于 2025-04-17 14:47

我正在写一个程序,用来验证和修正一个给定的日期字符串。比如说,我们有一个日期 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 个回答

1

这个解决方案非常复杂,我希望你能找到更好的方法。这个代码经过简单测试,可能已经足够用了。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))
2

一种可能的方法是构建一个正则表达式,它可以匹配任何内容,但会把好的匹配和坏的匹配放在不同的组里。通过检查结果中哪些组被填充,可以知道哪个组失败了。

>>> 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
1

我觉得你想要的事情可能做不到,因为 _sre 模块是用 C 语言写的 ;(。

你可以试试用 这个包,通过修改 sre_compile 的路径,先导入你新的 _sre 等等,但我觉得这样做不太值得。这个包是完全用 Python 写的 _sre 实现,所以你能看到源代码,修改它,并在下一个字符不匹配时做点什么。

你也可以通过以下方式做类似的事情:

  • 把日期字符串分成三部分(天、月和年),然后分别匹配正则表达式
  • 用其他不涉及正则表达式的方法来验证日期时间

也许你不能准确找到错误的数字,但在这种情况下,我觉得这没什么太大意义,只要你能告诉用户哪里出错了(是天、月还是年)。

撰写回答