6

我正在使用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。为了详细说明这一点,我想要以下有效的组合hmshmh、和(当然ms,不同段之间有数字)。作为奖励,正则表达式应该检查段中的十进制(即非自然)数字,并且只允许这些在最不重要的段中。ms

因此,除了最后一组之外,我都有一个数字匹配,例如:

([0-9]+)

最后一组甚至:

([0-9]*\.[0-9]+|[0-9]+(\.[0-9]*)?)  # to allow for .5 and 0.5 and 5.0 and 5

浏览 h、m 和 sa 可爱的小 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

4

5 回答 5

3

您可以分解您的正则表达式,使用符号h, m,s来表示每个子正则表达式,最基本的版本是:

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 的形式,因此在每个位置都使用小数进行解析,然后进行二次检查可能是最好的方法。

于 2012-07-02T12:07:08.017 回答
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
有效的组合是hms, hm, hs, h, ms,ms
or iow,可以省略分钟,但仍然有小时和秒。

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


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

所有这些 OR'd 一起为您提供了一个大但易于分解的正则表达式:

([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 答案的评论,我也会这样做(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

现在我们必须找到一种方法来匹配.xxdemical 表示

于 2012-07-02T12:14:29.563 回答
1

这是一个有效的简短 Python 表达式

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

受基于条件句的 Cameron Martins回答的启发。

解释:

(\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"
于 2012-07-02T12:50:46.397 回答
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))*/
于 2012-07-02T11:59:08.340 回答
0

最后一组应该是:

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

除非你想匹配5.


您可以使用正则表达式 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

我没有检查这是否有效,但它尝试仅匹配整数小时,分钟,然后首先匹配秒,然后如果最后匹配的是小时,它允许小数分钟,否则如果最后匹配的是分钟,它允许小数秒。

于 2012-07-02T12:10:37.057 回答