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

时间:2012-07-02 11:49:42

标签: regex parsing time lexical-analysis

我正在使用pyLR1编写自定义描述语言的词法规则,其中包括时间文字,例如:

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

小时,分钟和秒钟部分的规格顺序应固定为hms。要详细说明,我需要以下有效组合hmshmhmsms(带数字)当然不同的部分之间)。 作为奖励,正则表达式应检查段中的十进制(即非自然)数字,并且仅在具有最小重要性的段中允许这些数字。

所以我除了最后一组之外的所有人都有一个数字匹配:

([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的所有组合,一个可爱的小蟒蛇脚本给了我以下正则表达式:

(([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的答案。

5 个答案:

答案 0 :(得分:3)

您可以使用符号hms来表示正则表达式,以表示每个子目标,最基本的版本是:

h|hm|hms|ms|m|s

这是你目前所拥有的。你可以将其分解为:

(h|hm|hms)|(ms|m)|s

然后从第一个表达式中提取h,从第二个表达式中提取m(使用(x|) == x?):

h(m|ms)?|ms?|s

继续我们进入

h(ms?)?|ms?|s

这可能更简单(也可能是最简单的)。


添加正则表达式d来表示小数(如\.[0-9]+中所示),这可以写成

h(d|m(d|sd?)?)?|m(d|sd?)?|sd?

(即每个阶段可选择有小数,或者继续h ms的下一个。)

这会产生类似的东西(只需几小时和几分钟):

[0-9]+((\.[0-9]+)?h|h[0-9]+(\.[0-9]+)?m)|[0-9]+(\.[0-9]+)?m

考虑到这一点,可能无法进入一个适用于pyLR1的表单,因此在每个点进行小数点解析,然后进行二次检查可能是最好的方法。

答案 1 :(得分:1)

下面的表示应该是可以理解的,我不知道你正在使用的正确的正则表达式语法,所以你必须自己“翻译”到有效的语法。

你的营业时间

 [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}(\.[0-9]{1,3})?s)?
然而,

匹配的内容如下:10h30s
有效组合包括hmshm hs hmsm和{{ 1}}
或者说,分钟可以被省略,但仍然有几小时和几秒钟。

另一个问题是如果给出空字符串,则匹配,因为所有三个s都有效。所以你必须以某种方式解决这个问题。 HMM的


查看@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

所有这些OR一起给你一个很大但很容易打破的正则表达式:

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 的匹配问题。


关注@Donal Fellows对@dbaupp回答的评论,我也会做hs

(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 化学表示的方法

答案 2 :(得分:1)

这是一个简短的Python表达式that works

(\d+h)?(\d+m)?(\d*\.\d+|\d+(\.\d*)?)(?(2)s|(?(1)m|[hms]))

受Cameron Martins answer的启发,基于条件限制。

说明:

(\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"

答案 3 :(得分:0)

您可能有几小时,几分钟和几秒钟。

    /(\d{1,2}h)*(\d{1,2}m)*(\d{1,2}(\.\d+)*s)*/

应该做的工作。根据正则表达式库,您将按顺序获取项目,或者您必须进一步解析它们以检查h,m或s。

在后一种情况下,另请参阅

返回的内容
   /(\d{1,2}(h))*(\d{1,2}(m))*(\d{1,2}(\.\d+)*(s))*/

答案 4 :(得分:0)

最后一组应该是:

([0-9]*\.[0-9]+|[0-9]+(\.[0-9]+)?)

除非您想匹配5.


您可以使用regex ifs,如下所示:

(([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])?))

此处 - http://regexr.com?31dmj

我没有检查过这是否有效,但它只是将整数与小时,分钟,然后是秒匹配,然后如果匹配的最后一个是小时,则允许小数分钟,否则如果匹配的最后一个是分钟,它允许小数秒。