我希望用户提供一系列要打印的数字: E.g:
27-84
我想出了以下正则表达式:
^(?P<Begin>\d+)((-?)(?P<end>\d+)?)$
因此它将匹配:27-84
作为以下组:
Begin [0-2] `27`
2. [2-5] `-84`
3. [2-3] `-`
end [3-5] `84`
似乎没问题,但我想知道是否有更优雅的方式,或者是否有我正在监督的空洞。
27
。 27-
的单个号码应理解为27 to end
。-10
的单个号码应理解为begin to 10
。10-27
的序列应理解为包含10到27。答案 0 :(得分:6)
(以下原始答案)
好的,通过您的澄清更新,您将无法使用正则表达式轻松地 ,除非您也只接受-
作为“从开始到结束” 。在这种情况下,您可以使用:
>>> r = re.compile('^((?P<fixed>\d+)|(?P<begin>\d+)?-(?P<end>\d+)?)$')
>>> r.match('123').groupdict()
{'begin': None, 'fixed': '123', 'end': None}
>>> r.match('123-').groupdict()
{'begin': '123', 'fixed': None, 'end': None}
>>> r.match('-456').groupdict()
{'begin': None, 'fixed': None, 'end': '456'}
>>> r.match('-').groupdict()
{'begin': None, 'fixed': None, 'end': None}
>>> r.match('123-456').groupdict()
{'begin': '123', 'fixed': None, 'end': '456'}
否则,您将不得不使用更明确的案例:
>>> r = re.compile('^((?P<fixed>\d+)|-(?P<endonly>\d+)|(?P<begin>\d+)(?:-(?P<end>\d+)?))$')
>>> r.match('123').groupdict()
{'endonly': None, 'begin': None, 'fixed': '123', 'end': None}
>>> r.match('123-').groupdict()
{'endonly': None, 'begin': '123', 'fixed': None, 'end': None}
>>> r.match('-456').groupdict()
{'endonly': '456', 'begin': None, 'fixed': None, 'end': None}
>>> r.match('123-456').groupdict()
{'endonly': None, 'begin': '123', 'fixed': None, 'end': '456'}
>>> r.match('-') is None
True
正如您所看到的,之后正确解释结果所需的工作量会增加复杂性。
但是,我建议你不要使用正则表达式,并直接在Python中进行解析。例如:
def parseRange (expr):
if not expr or not re.match('^\d*-?\d*$', expr):
raise ValueError('Not a correct range')
# single number
if '-' not in expr:
return int(expr), int(expr)
else:
begin, end = expr.split('-')
begin = float('-inf') if begin == '' else int(begin)
end = float('inf') if end == '' else int(end)
return begin, end
像这样使用:
>>> parseRange('123')
(123, 123)
>>> parseRange('123-')
(123, inf)
>>> parseRange('-456')
(-inf, 456)
>>> parseRange('123-456')
(123, 456)
你可以把它们中的一些捕获组简单地删除:
^(?P<begin>\d+)-(?P<end>\d+)$
这也解决了-
可能没有第二个数字的问题,理论上(实际上)开始和结束可能没有任何区别。
如果您不总是希望匹配范围但又想要允许单个数字,则可以将范围部分放在非捕获组中。这样你就不会得到其他组:
^(?P<begin>\d+)(?:-(?P<end>\d+))?$
最后,如果你想接受to
作为分隔符,你甚至可以这样做:
^(?P<begin>\d+)(?:(?:-|\s+to\s+)(?P<end>\d+))?$
>>> r = re.compile('^(?P<begin>\d+)(?:-(?P<end>\d+))?$')
>>> r.match('123').groupdict()
{'begin': '123', 'end': None}
>>> r.match('123-456').groupdict()
{'begin': '123', 'end': '456'}
>>> r = re.compile('^(?P<begin>\d+)(?:(?:-|\s+to\s+)(?P<end>\d+))?$')
>>> r.match('123-456').groupdict()
{'begin': '123', 'end': '456'}
>>> r.match('123 to 456').groupdict()
{'begin': '123', 'end': '456'}
答案 1 :(得分:1)
要允许并正确处理您在编辑中提到的所有案例,您需要一个不同的正则表达式:
^(?=.*\d)(?P<begin>\d*)(?P<range>-?)(?P<end>\d*)$
开头的前瞻是必要的,以断言字符串中至少有一个数字(因为begin
或end
可以是可选的,但不是两者都可以。)
作为一个详细的正则表达式解释:
^ # Start of string
(?=.*\d) # Assert that there is at least one digit somewhere
(?P<begin>\d*) # Match 0 or more digits --> Begin
(?P<range>-?) # Match 0 or 1 dash
(?P<end>\d*) # Match 0 or more digits --> End
$ # End of string