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

时间:2013-02-05 09:02:37

标签: python regex

我正在编写一个程序来验证并将给定日期更正为字符串。让我们以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 个答案:

答案 0 :(得分:1)

我相信你想要的东西是不可能的,因为_sre模块是在 C 中实现的;(。

您可以尝试使用this package代替(通过猴子修补sre_compile,修改路径并首先导入新的_sre等),但我认为不值得它。它是完全用Python编写的_sre包的实现,因此您可以查看源代码,编辑它,并在下一个字符匹配时正确执行某些操作。< / p>

你可以通过以下两种方式做同样的事情:

  • 将日期字符串拆分为3(日,月和年)并独立匹配正则表达式
  • 使用不涉及正则表达式的其他方式验证日期时间

也许你没有获得错误所在的完全数字,但我不认为在这种情况下它太有意义,只要你告诉用户什么是错的(日,月或年)。

答案 1 :(得分:1)

这个解决方案是野兽,我希望你找到一个更好的方法。此代码经过轻微测试,可能已足够。 errorindex()函数将日期作为字符串,并返回错误条目的索引列表。如果第1个月的数字不正确,则存在歧义。如果不知道第一个数字,就无法确定第二个数字是否正确。这是代码。注意:我忘记了闰年了!

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 :(得分:0)

对我而言,最明显的答案是使用一些使用有限自动机或自己编写的正则表达式库。通过一些修改,您可以精确地确定失败的位置。但我认为这不是你愿意做的事情。

否则,如果您知道,输入将具有确切的大小,确切的日期格式,您可以将其划分为3个扇区 - dd mm yyyy,然后分别尝试分别为每个单个字符应用正则表达式。这不是一个很好的解决方案,但你会得到你想要的。

答案 3 :(得分:0)

一种可能的方法是构造一个匹配任何东西的正则表达式,但将好匹配和坏匹配放在不同的组中。检查结果中填充的组,以了解哪个组失败。

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