Regex 简化时间文字的正则表达式(如“10h50m”)
我正在为自定义描述语言编写lexer规则,其中应包括时间文字,例如:Regex 简化时间文字的正则表达式(如“10h50m”),regex,parsing,time,lexical-analysis,Regex,Parsing,Time,Lexical Analysis,我正在为自定义描述语言编写lexer规则,其中应包括时间文字,例如: 10h30m # meaning 10 hours + 30 minutes 5m30s # meaning 5 minutes + 30 seconds 10h20m15s # meaning 10 hours + 20 minutes + 15 seconds 15.6s # meaning 15.6 seconds 小时、分钟和秒部分的规格顺序应固定为h,m,s。为了详细说明这一点,我需要
10h30m # meaning 10 hours + 30 minutes
5m30s # meaning 5 minutes + 30 seconds
10h20m15s # meaning 10 hours + 20 minutes + 15 seconds
15.6s # meaning 15.6 seconds
小时、分钟和秒部分的规格顺序应固定为h
,m
,s
。为了详细说明这一点,我需要以下有效组合hms
,hm
,h
,ms
,m
和s
(当然,不同段之间有数字)。
作为奖励,正则表达式应该检查段中的十进制(即非自然数)数字,并且只允许在重要性最小的段中使用这些数字
因此,除了最后一组外,我还有一个数字匹配,如:
([0-9]+)
对于最后一组,甚至:
([0-9]*\.[0-9]+|[0-9]+(\.[0-9]*)?) # to allow for .5 and 0.5 and 5.0 and 5
通过查看h、m和s的所有组合,一个可爱的小python脚本为我提供了以下正则表达式:
(([0-9]*\.[0-9]+|[0-9]+(\.[0-9]*)?)h|([0-9]+)h([0-9]*\.[0-9]+|[0-9]+(\.[0-9]*)?)m|([0-9]+)h([0-9]+)m([0-9]*\.[0-9]+|[0-9]+(\.[0-9]*)?)s|([0-9]*\.[0-9]+|[0-9]+(\.[0-9]*)?)m|([0-9]+)m([0-9]*\.[0-9]+|[0-9]+(\.[0-9]*)?)s|([0-9]*\.[0-9]+|[0-9]+(\.[0-9]*)?)s)
显然,这是一个有点恐怖的表达。有什么办法可以简化这一点吗?答案必须与pythons
re
模块一起使用,如果由于正则表达式的限制,我也会接受与pyLR1
不兼容的答案。您可能有小时、分钟和秒
/(\d{1,2}h)*(\d{1,2}m)*(\d{1,2}(\.\d+)*s)*/
我应该做这项工作。根据regex库的不同,您将按顺序获取项目,或者您必须进一步解析它们以检查h、m或s
在后一种情况下,请参见
/(\d{1,2}(h))*(\d{1,2}(m))*(\d{1,2}(\.\d+)*(s))*/
您可以使用符号
h
,m
,s
对正则表达式进行分解,以表示每个子空间,最基本的版本是:
h|hm|hms|ms|m|s
这就是你现在拥有的。您可以将其分为:
(h|hm|hms)|(ms|m)|s
然后从第一个表达式中取出h
,从第二个表达式中取出m
(使用(x |)
=x?
):
继续我们到达
h(ms?)?|ms?|s
这可能更简单(也可能是最简单的)
在正则表达式
d
中添加小数(如\.[0-9]+
),这可以写成
h(d|m(d|sd?)?)?|m(d|sd?)?|sd?
(即,在每个阶段,可选地具有小数,或延续到下一个h
m
或s
)
这将导致类似(仅几个小时和几分钟):
考虑到这一点,可能不可能得到一个适用于pyLR1的形式,因此在每个点使用小数进行解析,然后进行二次检查可能是最好的方法。最后一组应该是:
([0-9]*\.[0-9]+|[0-9]+(\.[0-9]+)?)
除非您想匹配5。
你可以这样使用:
(([0-9]+h)?([0-9]+m)?([0-9]+s)?)(?(?<=h)(([0-9]*\.[0-9]+|[0-9]+(\.[0-9]*)?)m)?|(?(?<=m)(([0-9]*\.[0-9]+|[0-9]+(\.[0-9]*)?)s)?|\b(([0-9]*\.[0-9]+|[0-9]+(\.[0-9]*)?)[hms])?))
([0-9]+h)([0-9]+m)([0-9]+s)(?(下面的表示应该可以理解,我不知道您使用的确切正则表达式语法,因此您必须自己“翻译”为有效语法
你的时间
[0-9]{1,2}h
你的会议记录
[0-9]{1,2}m
你的秒
[0-9]{1,2}(\.[0-9]{1,3})?s
您希望所有这些都按顺序排列,并且能够省略其中任何一个(用换行?
)
但是,这与以下内容相匹配:10h30s
有效的组合是hms
,hm
,hs
,h
,ms
,m
和s
或者说,可以使用分钟,但仍有小时和秒
/(\d{1,2}h)*(\d{1,2}m)*(\d{1,2}(\.\d+)*s)*/
另一个问题是,如果给定了空字符串,则它是匹配的,因为所有三个?
都使其有效。因此,您必须设法解决这个问题。嗯
查看@dbaupph(ms?| ms?| s
,您可以选择上面的内容并进行匹配:
h: [0-9]{1,2}h
m: [0-9]{1,2}m
s: [0-9]{1,2}(\.[0-9]{1,3})?s
因此,您可以:
h(ms?)?: ([0-9]{1,2}h([0-9]{1,2}m([0-9]{1,2}(\.[0-9]{1,3})?s)?)?
ms? : [0-9]{1,2}m([0-9]{1,2}(\.[0-9]{1,3})?s)?
s : [0-9]{1,2}(\.[0-9]{1,3})?s
所有这些都会给你一个大但容易分解的正则表达式:
([0-9]{1,2}h([0-9]{1,2}m([0-9]{1,2}(\.[0-9]{1,3})?s)?)?|[0-9]{1,2}m([0-9]{1,2}(\.[0-9]{1,3})?s)?|[0-9]{1,2}(\.[0-9]{1,3})?s
这就解决了空字符串问题和匹配hs
的问题
看看@Donal Fellows对@dbaupp answer的评论,我也会做(h?m)?S|h?m|h
(h?m)?s: (([0-9]{1,2}h)?[0-9]{1,2}m)?[0-9]{1,2}(\.[0-9]{1,3})?s
h?m : ([0-9]{1,2}h)?[0-9]{1,2}m
h : [0-9]{1,2}h
并合并在一起,最终会得到比上面更小的东西:
(([0-9]{1,2}h)?[0-9]{1,2}m)?[0-9]{1,2}(\.[0-9]{1,3})?s|([0-9]{1,2}h)?[0-9]{1,2}m|[0-9]{1,2}h
现在我们必须找到一种方法来匹配.xx
局部表示法下面是一个简短的Python表达式:
灵感来源于基于条件的Cameron Martins
解释:
这不是在解析10h10h10h
?@dbaupp它是:(.在对语言进行词法分析/解析时,这样的文字会出现语法错误。当然,您必须将其绑定到整个值:^…$-否则它还会解析“现在是10h30m,早上好”:-)。在开头加一个“^”,在结尾加一个$以匹配完整的值。它是lexer中的一个正则表达式。如果第一个h
不在正则表达式中,lexer无法知道它必须在第一个h
之后中止。lexer理解什么样的正则表达式?完整的Python正则表达式还是某种受限的?它有点受限,但我认为它可以是exp如有必要,我会在某些区域与Python正则表达式进行AND。我也会接受与Python的re
一起工作的答案,即使它们(尚未)与Pyr1一起工作。此外,还有类似“10H30”的字符串(即:ommiting minutes)允许?@c00kiemon5ter最初不应该。但是我认为如果。这不包括允许最低有效段中的十进制值(仅)的点,或者我错了吗?但是,我可以在以后的检查中实现这一点(即匹配RE后,我可以尝试将非最低有效段转换为整数).由于人们希望特别区分最不重要的值,我会考虑(h?m)?s|h?m | h
(其中大写版本是允许小数的版本)。@DonalFellows,这是一种更好的方法:从右分解,而不是从左分解。:)@DonalFellows:你的解决方案似乎很有效。我想给你一个肯定的评价,但我恐怕现在我不能这么做,除非对你的评论投上更高的票(有没有办法在一个肯定的答案上进行合作?)。它在第一个小数点后中止解析,这并不完美,但对我来说没问题。@Jonas我不需要代表;这只是一个基于dbaupp回答的观察结果。我自己
(h?m)?s: (([0-9]{1,2}h)?[0-9]{1,2}m)?[0-9]{1,2}(\.[0-9]{1,3})?s
h?m : ([0-9]{1,2}h)?[0-9]{1,2}m
h : [0-9]{1,2}h
(([0-9]{1,2}h)?[0-9]{1,2}m)?[0-9]{1,2}(\.[0-9]{1,3})?s|([0-9]{1,2}h)?[0-9]{1,2}m|[0-9]{1,2}h
(\d+h)?(\d+m)?(\d*\.\d+|\d+(\.\d*)?)(?(2)s|(?(1)m|[hms]))
(\d+h)? # optional int "h" (capture 1)
(\d+m)? # optional int "m" (capture 2)
(\d*\.\d+|\d+(\.\d*)?) # int or decimal
(?(2) # if "m" (capture 2) was matched:
s # "s"
| (?(1) # else if "h" (capture 1) was matched:
m # "m"
| # else (nothing matched):
[hms])) # any of the "h", "m" or "s"