我正在实现一个多范围验证器,它可以同时验证多个范围的用户输入。
示例
如果我定义范围[1.5,4] | [6.9,9.3] | [10,11],它的读数如下:从1到4,或从6到9不包括在内。 在这种情况下,数字5,9.3,13将不包括在该范围内,但数字2,7和10将包括在内。
表达式可以包含尽可能多的格式:
[或(
浮点数或整数
,
浮点数或整数
]或)
表达式语法
基本上,这个范围表达式由:
组成'['包含范围的开始
']包含范围的结束
'('exlusive start of range
')'独家结束范围
','值分隔符
'|'或声明
现在,我需要一种方法来验证用户是否编写了正确的范围表达式。如果解析器读取格式不正确的表达式,则解析器将抛出异常。我想创建一个正则表达式来非常优雅地验证用户输入的表达式。我对正则表达式的体验并不是那么广泛,这似乎是一个非常难以制作的。我想要一些关于如何构造一个复杂的正则表达式的指导,这个正则表达式有整数,双精度,'['等等。
虽然正则表达式与平台无关,但我正在使用.NET。
答案 0 :(得分:2)
一个快速镜头,自由间距,可能远没有效率(首先是因为单个范围会使正则表达式在发现后面没有'|'时回溯) - 请参阅下面的版本2,这是(我相信)效率更高:
^ # Start of string
(?: # Start group, non-capturing
[([] # '(' or '['
\s* # optional whitespace
[0-9]+ # at least one digit 0-9
(?:\.[0-9]+)? # optionally '.' followed by at least one digit 0-9
\s* # optional whitespace
, # ','
\s* # optional whitespace
[0-9]+ # at least one digit 0-9
(?:\.[0-9]+)? # optionally '.' followed by at least one digit 0-9
\s* # optional whitespace
[)\]] # ')' or ']'
\s* # optional whitespace
\| # '|'
\s* # optional whitespace
)* # all the above may appear 0 or more times
[([] # The remainder is exactly the same as the group above,
\s* # used for a single range or the last range -
[0-9]+ # i.e., a range NOT followed by '|' - of a multi range.
(?:\.[0-9]+)?
\s*
,
\s*
[0-9]+
(?:\.[0-9]+)?
\s*
[)\]]
$ # end of string
这将匹配例如:
[1.5, 3]
[23.7, 3.70)
[2.9 , 3]|[3,2)
[1.5, 4] | [6.9, 9.3) | [10, 11]
(1.5, 3]
[23.7, 3.70)
(1.5, 5.0)
但不是:
[23.7, 3.70) | (7, 9) | // trailing OR
| [23.7, 3.7] // leading OR
请注意,它不能确保第二个数字实际上高于第一个数字。为此,我确实建议将其留给/解析器 - 或添加捕获组并在正则表达式之外处理它们。
版本2
由于回溯较少,这应该更有效 - 它基本上从以下变化:
(任意数量的范围后跟|)后跟一个范围
......来:
一个范围后跟(任意数量的范围以|开头)
ETA:为了解释,版本1开始检查“范围后跟|”。
如果我们只有一个范围,那就浪费了时间。当它到达“|”它将重新开始,检查正则表达式的第二部分 - 即是否存在所需的“范围没有|”?
在版本2中,我们开始只检查“范围”。这意味着,如果只有一个范围,它将成功,没有任何回溯。如果我们给它乱码,例如hello
,它会立即失败,因为它现在知道第一个字符必须是(
或[
- 它不是可选的。而在版本1中,因为第一部分是可选的,所以它必须检查正则表达式的第二部分以确保失败。
在几乎所有其他情况下(我已经测试过),版本2匹配 - 或者无法匹配 - 只需更少的步骤。
在这里,因为它基本上是相同的正则表达式切换了一些部分,我会在评论中添加一个示例匹配:
^
[([] # (
\s* #
[0-9]+ # 3
(?:\.[0-9]+)? # .90
\s* #
, # ,
\s* #
[0-9]+ # 43
(?:\.[0-9]+)? # .2
\s* #
[)\]] # ]
#
(?: #
\s* #
\| # |
\s* #
#
[([] # [
\s* #
[0-9]+ # 55
(?:\.[0-9]+)? # .20
\s* #
, # ,
\s* #
[0-9]+ # 2
(?:\.[0-9]+)? # .91
\s* #
[)\]] # )
)*
$
匹配和不匹配应与版本1相同。
答案 1 :(得分:1)
如果您选择使用正则表达式,则必须描述使用|
分隔的所有可能性。这里是第一个范围的示例(您可以轻松地为其他范围添加值):
@"1\.[5-9]|[23](?:\.[0-9])?|4"
但IMO最优雅的方式是提取所有数字并将其作为数字进行测试。