[英]Regular expression for dates in R
我正在尝试在R中创建一个正则表达式,以在某些文本中搜索日期。 由于我无法控制实际的日期格式,因此我试图“捕获”所有可能的dd / mm / yy格式(一个或两位数的月份,两位或四位的年份,可选的一位或两位的日期以及一系列分隔符) (“ /”,“-”,“。”),可能包含空格)。
到目前为止,我的正则表达式是:
pattern = "(\\d{0,2}[/\\.-])?[ ]?(\\d{1,2}[ ]*[/\\.-]|January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Febr|Mar|Apr|Jun|Jul|Aug|Sept|Sep|Oct|Nov|Dec|Jan\\.|Feb\\.|Febr\\.|Mar\\.|Apr\\.|Jun\\.|Jul\\.|Aug\\.|Sept\\.|Sep\\.|Oct\\.|Nov\\.|Dec\\.)[ ]*[']?\\d{2,4}"
这似乎适用于大多数格式,但是它包含一个我很难理解的错误:
str_extract_all("09/11 /1985", pattern = pattern) # returns: "09/11 /1985"
str_extract_all(" 09/11 /1985", pattern = pattern) # returns: c("09/11", "1985")
这听起来很奇怪。 由于我不包括环顾四周,因此开始时的额外空间应该没有影响。 结果不然。 我究竟做错了什么?
问题出在正则表达式的第一部分,您可能会尝试匹配日期: (\\\\d{0,2}[/\\\\.-])?[ ]?
您可以选择匹配0到2天,然后匹配其中一个定界符。 然后可以选择匹配一个空格。
在09/11 /1985
9月11日的情况下,此部分与前导空格匹配,因此将09匹配为月,将11匹配为年。
要摆脱这种行为,您应该将空格移到可选组中。 您可能还希望匹配1或2位数字,否则它将匹配前导定界符。
因此,我会将第一部分重写为(\\\\d{1,2}[/\\\\.-][ ]?)?
您还可以改善其他几点,例如:
January|Jan|Jan\\\\.
与Jan(?:\\\\.|uary)?
我认为最好的办法是在读取文件之前先了解给定字符串中使用的日期格式,然后测试日期格式是否始终符合预期。 但是,正如OP所述,情况并非如此。 这不是日期格式的详尽列表,但应该给您一种印象,即找出仅允许有效日期的正则表达式可能是一件繁琐的工作。 另外,格式猜测可能会使不了解细节的人难以预测您的脚本。
如果您仍然认为您需要对不同的日期格式使用正则表达式,请尝试以一种易于读者理解的方式设计正则表达式:
(?:format1)|(?:format2)|...|(?:formatN)
在这种情况下,format1的优先级高于
https://stackoverflow.com/a/15504877/6018688上也有相当不错的正则表达式,即使对checking年dd/mm/yyyy
, dd-mm-yyyy
或dd.mm.yyyy
计算,它们也可以很好地检查这些格式的日期有效性。 dd.mm.yyyy
。
^(?:(?:31(\\/|-|\\.)(?:0?[13578]|1[02]))\\1|(?:(?:29|30)(\\/|-|\\.)(?:0?[1,3-9]|1[0-2])\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$|^(?:29(\\/|-|\\.)0?2\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\\d|2[0-8])(\\/|-|\\.)(?:(?:0?[1-9])|(?:1[0-2]))\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$
在同一个问题中,使用月名称的不同答案:
^(?:(?:31(\\/|-|\\.)(?:0?[13578]|1[02]|(?:Jan|Mar|May|Jul|Aug|Oct|Dec)))\\1|(?:(?:29|30)(\\/|-|\\.)(?:0?[1,3-9]|1[0-2]|(?:Jan|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$|^(?:29(\\/|-|\\.)(?:0?2|(?:Feb))\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\\d|2[0-8])(\\/|-|\\.)(?:(?:0?[1-9]|(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep))|(?:1[0-2]|(?:Oct|Nov|Dec)))\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$
我认为您现在已经有了印象,编写一个实际上可以完成您打算做的事情的正则表达式是多么令人费解。 我真的会尽量将允许的日期保持在最低限度,并寻求一个限制性很强的正则表达式。 在您的示例中,您只给出了仅包含日期(和空格)的字符串,而没有其他内容。 如果是这种情况,则应尝试使用"^yourregex$"
对整个字符串进行数学运算,如果要在字符串的开头和结尾"^\\s*yourregex\\s*$"
空格,请使用"^\\s*yourregex\\s*$"
。 由于您在字符串的开头有一个带空格的示例,因此我将使用后者进行进一步的开发。
在您的情况下,我将仅以几年开始:
"^\\\\s*(?:\\\\d{4})\\\\s*$"
然后允许使用其他东西mm-dd-YY(不检查它是否确实是有效日期或“ 33-13-2016”,但也可以使用两位数的年份)
"(?:\\\\d{1,2}[/.-]\\\\d{1,2}[/.-](?:\\\\d{4}|\\\\d{2})"
如果要在定界符之间留出空间:
"(?:\\\\d{1,2}\\\\s*[/.-]\\\\s*\\\\d{1,2}\\\\s*[/.-]\\\\s*\\\\d{4})"
然后使用书面或缩写月份名称进行格式化:
"(\\\\d{1,2}\\\\s*[/.-]?\\\\s*(?:January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Febr|Mar|Apr|Jun|Jul|Aug|Sept|Sep|Oct|Nov|Dec|Jan\\\\.|Feb\\\\.|Febr\\\\.|Mar\\\\.|Apr\\\\.|Jun\\\\.|Jul\\\\.|Aug\\\\.|Sept\\\\.|Sep\\\\.|Oct\\\\.|Nov\\\\.|Dec\\\\.)\\\\s*[/.-]?\\\\s*(?:'?\\\\d{2}|\\\\d{4}))"
放在一起:
"^\\\\s*(?:\\\\d{4}$)|(?:\\\\d{1,2}\\\\s*[/.-]\\\\s*\\\\d{1,2}\\\\s*[/.-]\\\\s*\\\\d{4})|(\\\\d{1,2}\\\\s*[/.-]?\\\\s*(?:January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Febr|Mar|Apr|Jun|Jul|Aug|Sept|Sep|Oct|Nov|Dec|Jan\\\\.|Feb\\\\.|Febr\\\\.|Mar\\\\.|Apr\\\\.|Jun\\\\.|Jul\\\\.|Aug\\\\.|Sept\\\\.|Sep\\\\.|Oct\\\\.|Nov\\\\.|Dec\\\\.)\\\\s*[/.-]?\\\\s*(?:'?\\\\d{2}|\\\\d{4}))\\\\s*$"
这样,您可以根据需要链接任意多种格式。
请将以下正则表达式与您的正则表达式进行比较,以检查不同输入字符串上的行为。 我添加了字边界\\b
约束,因为您使用了str_extract_all,所以我认为同一字符串中可以有多个日期。
string = "only a year 1985. No space 2.Jan.2016. 2. Jan. 2016. 2. Jan. '16 2/1/16 02/01/2016 19855 ID1985A 2. Jan 2016 2.. Jan 2016 1January2016 2-Jan.-2016 2-Jan-2016 2.\tJan.\t2016"
pattern = "(\\d{1,2}[/\\.-][ ]?)?(\\d{1,2}[ ]*[/\\.-]|January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Febr|Mar|Apr|Jun|Jul|Aug|Sept|Sep|Oct|Nov|Dec|Jan\\.|Feb\\.|Febr\\.|Mar\\.|Apr\\.|Jun\\.|Jul\\.|Aug\\.|Sept\\.|Sep\\.|Oct\\.|Nov\\.|Dec\\.)[ ]*[']?\\d{2,4}"
p="\\s*(?:\\b\\d{4}\\b)|(?:\\b\\d{1,2}\\s*[/\\.-]\\s*\\d{1,2}\\s*[/\\.-]\\s*(?:\\d{4}|\\d{2})\\b)|\\b\\d{1,2}\\s*[/\\.-]?\\s*(?:January|February|March|April|May|June|July|August|September|October|November|December|(?:Jan|Feb|Febr|Mar|Apr|Jun|Jul|Aug|Sept|Sep|Oct|Nov|Dec).?)\\s*[/\\.-]?\\s*(?:\\d{4}|'?\\d{2})\\b\\s*"
str_extract_all(string, pattern=pattern)
str_extract_all(string, pattern=p)
一个警告:当允许带有空格的不同格式的多个版本时,您将允许难以保证仅日期匹配且文本中不包含其他数字值的差异。
不需要在字符组中转义点,因为[\\。]中的点只能是[。]; 除非您还希望以反斜杠作为day \\ mont \\ year之间的分隔符。 当输入格式可变时,空格也可以是\\t
制表符,因此用\\s
替换[ ]
(匹配除\\n
等行终止符之外的任何空格字符)似乎是一个好主意。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.