加入多个正则表达式以提高可读性

时间:2017-10-03 11:43:35

标签: python regex

我在日期中有以下要求,可以是以下任何一种格式。

mm / dd / yyyy或dd Mon YYYY

下面显示的例子很少 2009年4月20日和2001年1月24日

为了解决这个问题,我写了正则表达式,如下所示

下面列出了很少的文字场景

  

txt1 = 'Lithium 0.25 (7/11/77). LFTS wnl. Urine tox neg. Serum tox + fluoxetine 500; otherwise neg. TSH 3.28. BUN/Cr: 16/0.83. Lipids unremarkable. B12 363, Folate >20. CBC: 4.9/36/308 Pertinent Medical Review of Systems Constitutional:

     

txt2 =“s患者是一名44岁的已婚白人女性,   失业的装饰员,和丈夫一起生活,照顾两个年轻人   儿童,由国会山医院PCP转介,Heather博士   Zubia,在第一次访问托尼博士之前进行紧急评估/治疗   Winkler在2001年1月24日的八个星期。“

date = re.findall(r'(?:\b(?<!\.)[\d{0,2}]+)'
                            '(?:[/-]\d{0,}[/-]\d{2,4}) | (?:\b(?<!\.)[\d{1,2}]+)[th|st|nd]*'
                            ' (?:[Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec][a-z]*) \d{2,4}', txtData)

我没有得到2001年1月24日,好像我单独运行(?:\b(?<!\.)[\d{1,2}]+)[th|st|nd]* (?:[Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec][a-z]*) \d{2,4}'我能够得到输出。

问题1:上述表达式中的错误是什么?

问题2:我想将两者结合起来使其更具可读性,因为我必须解析任何其他格式,所以我使用了如下所示的连接

RE1 = '(?:\b(?<!\.)[\d{0,2}]+) (?:[/-]\d{0,}[/-]\d{2,4})'
RE2 = '(?:\b(?<!\.)[\d{1,2}]+)[th|st|nd]* (?:[Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec][a-z]*) \d{2,4}'

regex_all = '|'.join([RE1, RE2])
regex_all = re.compile(regex_all)

date = regex_all.findall(txtData) // notice here txtData can be any one of the above string.

在上述情况下,我的输出为NaN。

如果我加入,请说明错误是什么。

感谢您的帮助。

3 个答案:

答案 0 :(得分:2)

请注意,加入在字符串中的同一位置也匹配的长模式是一个非常糟糕的主意。这将导致正则表达式引擎回溯太多,并可能导致崩溃和减速。如果有办法重新编写交替,以便它们只能在不同位置匹配,甚至完全摆脱它们,那就去做吧。

此外,您应该使用分组构造(...)对模式序列进行分组,并在需要匹配特定字符时仅使用[...]个字符类。

此外,您的替代方案是重叠的,您可以轻松地将它们组合起来。请参阅固定的正则表达式:

\b(?<!\.)\d{1,2}(?:[/-]\d+[/-]|(?:th|st|[nr]d)?\s*(?:(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*))\s*(?:\d{4}|\d{2})\b

请参阅regex demo

<强>详情

  • \b - 字边界
  • (?<!\.) - 紧靠当前位置左侧的.
  • \d{1,2} - 1或2位数字
  • (?: - 非捕获交替组的开始:
    • [/-]\d+[/-] - /-,1位数,-/
    • | - 或
    • (?:th|st|[nr]d)?\s*(?: (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*)) - thstndrd(可选),后跟0 +空格,然后是月份名称
  • \s* - 0+ whitespaces
  • (?:\d{4}|\d{2}) - 2位或4位
  • \b - 尾随字边界。

另一个注意事项:如果要将类似日期的字符串与两个匹配的分隔符匹配,则需要捕获第一个分隔符,并使用反向引用来匹配第二个分隔符,请参阅this regex demo。在Python中,您需要re.finditer才能获得这些匹配。

请参阅this Python demo

import re
rx = r"\b(?<!\.)\d{1,2}(?:([/-])\d+\1|(?:th|st|[nr]d)?\s*(?:(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*))\s*(?:\d{4}|\d{4})\b"
s = "Lithium 0.25 (7/11/77).  LFTS wnl.  Urine tox neg.  Serum tox\nfluoxetine 500; otherwise neg.  TSH 3.28.  BUN/Cr: 16/0.83.  Lipids unremarkable.  B12 363, Folate >20.  CBC: 4.9/36/308 Pertinent Medical\nReview of Systems Constitutional:\n\nThe patient is a 44 year old married Caucasian woman, unemployed Decorator, living with husband and caring for two young children, who is referred by Capitol Hill Hospital PCP, Dr. Heather Zubia, for urgent evaluation/treatment till first visit with Dr. Toney Winkler IN EIGHT WEEKS on 24 Jan 2001"
print([x.group(0) for x in re.finditer(rx, s, re.I)])
# => ['7/11/77', '24 Jan 2001']

答案 1 :(得分:1)

r'(?:\b(?<!\.)[\d{0,2}]+)'
 '(?:[/-]\d{0,}[/-]\d{2,4}) | (?:\b(?<!\.)[\d{1,2}]+)[th|st|nd]*'
 ' (?:[Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec][a-z]*) \d{2,4}'
  • 你应该为每个字符串使用原始字符串(r'foo'),而不仅仅是第一个字符串。这样反斜杠(\)将被视为普通字符,可供re库使用。
  • [abc|def]匹配[]之间的任何字符,而(one|two|three)匹配任何表达式(onetwothree

答案 2 :(得分:1)

我认为你的方法太复杂了。我建议使用简单的正则表达式和strptime()的组合。

import re
from datetime import datetime

date_formats = ['%m/%d/%Y', '%d %b %Y']
pattern = re.compile(r'\b(\d\d?/\d\d?/\d{4}|\d\d? \w{3} \d{4})\b')

data = "... your string ..."

for match in re.findall(pattern, data):
    print("Trying to parse '%s'" % match)
    for fmt in date_formats:
        try:
            date = datetime.strptime(match, fmt)
            print(" OK:", date)
            break
        except:
            pass

这种方法的优点是,除了更容易管理的正则表达式之外,它不会选择看似合理但不存在的日期,例如2/29/2000(而2/29/2004可以工作)。