Regex 简化时间文字的正则表达式(如“10h50m”)

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。为了详细说明这一点,我需要

我正在为自定义描述语言编写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
。为了详细说明这一点,我需要以下有效组合
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)*/
另一个问题是,如果给定了空字符串,则它是匹配的,因为所有三个
都使其有效。因此,您必须设法解决这个问题。嗯


查看@dbaupp
h(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"