3

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

4 回答 4

1

我相信你想要什么是不可能的,因为_sre模块是用C实现的;(。

你可以尝试使用这个包(通过猴子补丁sre_compile、修改路径和_sre首先导入你的新包等),但我认为它不值得。它是_sre完全用 Python 编写的包的实现,因此您将能够查看源代码、对其进行编辑,并在下一个字符不匹配时做一些正确的事情。

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

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

也许您没有获得错误所在的确切数字,但我认为在这种情况下它没有太大意义,只要您告诉用户出了什么问题(日、月或年)。

于 2013-02-05T09:54:44.887 回答
1

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

>>> 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
于 2013-02-05T10:06:24.463 回答
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))
于 2013-02-05T10:32:59.430 回答
0

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

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

于 2013-02-05T09:47:46.313 回答