0

我试图理解以下正则表达式规则:

import re

time_format = r"(?:(?P<weeks>\d+)\W*(?:weeks?|w),?)?\W*(?:(?P<days>\d+)\W*(?:days?|d),?)?\W*(?:(?P<hours>\d+):(?P<minutes>\d+)(?::(?P<seconds>\d+)(?:\.(?P<microseconds>\d+))?)?)?"
time_matcher = re.compile(time_format)
time_matches = time_matcher.match(td_str)

使用此规则,如果我设置,td_str = '0:10'我会得到以下结果:

{'days': None,
 'hours': '0',
 'microseconds': None,
 'minutes': '01',
 'seconds': None,
 'weeks': None}

如果我设置td_str = '0:0:10',我会得到以下结果:

{'days': None,
 'hours': '0',
 'microseconds': None,
 'minutes': '0',
 'seconds': '01',
 'weeks': None}

我如何更改正则表达式规则,以便0:10将其解释为 0 分钟 + 10 秒?此外,“1:20:1”应解释为 1 小时 + 20 分钟 + 1 秒。

所以我想要创建的正则表达式规则(据我了解正则表达式)是:[H:[M:]]S

EDIT1:我相信我已经为以下内容构建了正确的规则[M:]S

time_format = r"((?P<minutes>\d+)?:?)(?P<seconds>\d+)"

任何人都可以确认这是正确的做法吗?

EDIT2:扩展Edit1中显示的规则,以下确实有效(有时):

time_format = r"((((?P<hours>\d+)?:?)(?P<minutes>\d+))?:?)(?P<seconds>\d+)"

但是,如果我说time='1:10',那么这个 get 会错误地转换为 1 小时 1 分 0 秒,而不是 1 分 10 秒。

EDIT3:这就是我现在解决问题的方法,而不是使用正则表达式。我仍然很想知道如何使用正则表达式来完成同样的任务。

# defaults
days = 0
hours = 0
minutes = 0
seconds = 0
microseconds = 0

split_fields = time_string.split(':')
nbr_fields = len(split_fields)

if nbr_fields == 0: # should never happen
    pass
if nbr_fields == 1:
    seconds = int(split_fields[0])
elif nbr_fields == 2:
    minutes = int(split_fields[0])
    seconds = int(split_fields[1])
elif nbr_fields == 3:
    hours = int(split_fields[0])
    minutes = int(split_fields[1])
    seconds = int(split_fields[2])
else: # in case there's more than 3 fields ...
    hours = int(split_fields[-3])
    minutes = int(split_fields[-2])
    seconds = int(split_fields[-1])
4

1 回答 1

0

正则表达式匹配秒的部分是可选的,因此可以按照您指定的以下?字符进行匹配。这同样适用于微秒字段。

只取H:M[:S.[USEC]]正则表达式的一部分会产生如下内容:

(?P<hours>\d+):(?P<minutes>\d+)(?::(?P<seconds>\d+)(\.(?P<microseconds>\d+))?)?

并不总是需要使用正则表达式来进行这种匹配。有时更容易编写自己的解析器来拆分元素,例如使用string.split(':'). 稍后回来阅读代码可能会更容易理解。

(我刚刚注意到您在秒和微秒之间有一个冒号。上面列出的正则表达式必须更改以说明这一点。列出的正则表达式将匹配 01:02:03.456。

编辑:

可以将您的正则表达式构建为(S)|(M:S)|(H:M:S),但这不适用于命名组,因为组名不能出现多次。问题是您希望引擎向前看并首先匹配最右边的令牌,然后再匹配左侧的令牌。字符串将从左到右扫描以查找匹配项,因此无法以明确的方式描述字段,至少在使用命名组时不会。

另一个不涉及命名组的解决方案是使用更通用的表达式,例如(\d+)(:\d+)?(:\d+)?然后查看返回的非 None 组以确定它们的含义。如果有 1 组,则只有 S,如果有 2,则 M:S 等。

于 2012-08-28T11:14:09.150 回答