确保两个正则表达式找不到相同的结果

时间:2012-11-22 20:58:44

标签: python regex

我正在尝试从字符串中解析出所有日期(可能以不同的形式编写)。问题是可能有一个以d/m -y格式写的日期,例如22/11 -12。但也可能有一个以d/m形式写的日期,没有指定年份。如果我在此字符串中找到包含较长形式的日期,我不希望以较短的形式再次找到它。这是我的代码失败的地方,它找到了第一个日期两次(一年一次,一次没有它)。

我真的有两个问题:(1)这样做的“正确”方法是什么。我似乎从错误的角度来看待这个问题。 (2)如果我坚持这样做,这条线datestring.replace(match.group(0), '')怎么不删除日期,所以我再也找不到了?

这是我的代码:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import re

dformats = (
    '(?P<day>\d{1,2})/(?P<month>\d{1,2}) -(?P<year>\d{2})',
    '(?P<day>\d{1,2})/(?P<month>\d{1,2})',
    '(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})',
            )


def get_dates(datestring):
    """Try to extract all dates from certain strings.

    Arguments:
    - `datestring`: A string containing dates.
    """
    global dformats

    found_dates = []

    for regex in dformats:
        matches = re.finditer(regex, datestring)
        for match in matches:
            # Is supposed to make sure the same date is not found twice
            datestring.replace(match.group(0), '')

            found_dates.append(match)
    return found_dates

if __name__ == '__main__':
    dates = get_dates('1/2 -13, 5/3 & 2012-11-22')
    for date in dates:
        print date.groups()

3 个答案:

答案 0 :(得分:2)

两种方式:

  1. 使用单个正则表达式并使用|运营商将您的所有案例加在一起:

    expr = re.compile ( r"expr1|expr2|expr3" )

  2. 仅查找单个实例,然后为下一次搜索传递“开始位置”。请注意,这会使事情变得复杂,因为无论选择哪种格式,您都希望始终以最早的匹配开始。即,遍历所有三个匹配,找出哪个是最早的匹配,进行替换,然后使用递增的起始位置再次执行。这使得选项1更容易,无论如何。

  3. 还有几点:

    1. 确保使用“原始字符串”:在每个字符串的前面加上“r”。否则,'\'字符可能会被吃掉而不会传递给RE引擎

    2. 考虑使用“sub”和回调函数代替“repl”参数来进行替换,而不是使用finditer。在这种情况下,“repl”传递一个匹配对象,并应返回替换字符串。

    3. 如果未选择该替代方案,则“单个”中的匹配组将具有值None,从而可以轻松检测使用的替代方案。

    4. 除非您打算修改该变量,否则不应该说“全局”。

    5. 这是一些完整的,有效的代码。

      #!/usr/bin/env python
      # -*- coding: utf-8 -*-
      
      import re
      
      expr = re.compile(
          r'(?P<day1>\d{1,2})/(?P<month1>\d{1,2}) -(?P<year>\d{2})|(?P<day2>\d{1,2})/(?P<month2>\d{1,2})|(?P<year3>\d{4})-(?P<month3>\d{2})-(?P<day3>\d{2})')
      
      
      def get_dates(datestring):
          """Try to extract all dates from certain strings.
      
          Arguments:
          - `datestring`: A string containing dates.
          """
      
          found_dates = []
          matches = expr.finditer(datestring)
          for match in matches:
              if match.group('day1'):
                  found_dates.append({'day': match.group('day1'),
                                       'month': match.group('month1') })
              elif match.group('day2'):
                  found_dates.append({'day': match.group('day2'),
                                      'month': match.group('month2')})
              elif match.group('day3'):
                  found_dates.append({'day': match.group('day3'),
                                      'month': match.group('month3'),
                                      'year': match.group('year3')})
              else:
                  raise Exception("wtf?")
          return found_dates
      
      if __name__ == '__main__':
          dates = get_dates('1/2 -13, 5/3 & 2012-11-22')
          for date in dates:
              print date
      

答案 1 :(得分:2)

您可以在第二个正则表达式中使用negative look ahead仅匹配dates未跟随的-year: -

dformats = (
    r'(?P<day>\d{1,2})/(?P<month>\d{1,2}) -(?P<year>\d{2})',
    r'(?P<day>\d{1,2})/(?P<month>\d{1,2})(?!\s+-(?P<year>\d{2}))',
    r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
)

因此,first正则表达式中匹配的日期将不会与第二个匹配。

答案 2 :(得分:1)

您可以sub代替find

def find_dates(s):

    dformats = (
        '(?P<day>\d{1,2})/(?P<month>\d{1,2}) -(?P<year>\d{2})',
        '(?P<day>\d{1,2})/(?P<month>\d{1,2})',
        '(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})',
    )    

    dates = []
    for f in dformats:
        s = re.sub(f, lambda m: dates.append(m.groupdict()), s)
    return dates