0

我正在实现一个多范围验证器,它可以一次针对多个范围验证用户输入。

例子

如果我定义范围 [1.5, 4] | [6.9, 9.3) | [10, 11],它会读作:从 1 到 4,或从 6 到 9 不包括在内。在这种情况下,数字 5、9.3、13 将不包含在此范围内,但数字 2、7 和 10 将包含在此范围内。

表达式可以有尽可能多的范围,格式如下:

[ 或者 (

浮点数或整数

,

浮点数或整数

] 或者 )

表达式语法

本质上,这个范围表达式由以下部分组成:

'[' 包括范围的开始

'] 包含范围的结束

'(' 独占范围的开始

')' 独占范围结束

',' 值分隔符

'|' 或声明

现在,我需要一种方法来验证用户是否编写了正确的范围表达式。如果解析器读取格式不正确的表达式,解析器将抛出异常。我想创建一个正则表达式来非常优雅地验证用户输入的表达式。我对正则表达式的经验并不广泛,这似乎很难制作。我想要一些关于如何构造一个复杂的正则表达式的指导,它有整数、双精度、'['等。

尽管正则表达式与平台无关,但我正在使用 .NET。

4

2 回答 2

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

请注意,它不能确保第二个数字实际上高于第一个。为此,我真的建议将其留给 /a 解析器 - 或添加捕获组并在正则表达式之外处理它们。

版本 2

由于更少的回溯,这应该更有效 - 它基本上是从:

(任意数量的范围后跟|)后跟一个范围

... 至:

后跟的范围(任意数量的以 | 开头的范围)

ETA:解释一下,版本 1 开始检查“后跟 | 的范围”。

如果我们只有一个范围,那就是浪费时间。当它到达“|” 它将重新开始,检查正则表达式的第二部分 - 即是否存在所需的“没有|的范围”?

相反,在版本 2 中,我们开始检查简单的“范围”。这意味着,如果只有一个范围,它将成功而无需任何回溯。如果我们给它乱码,例如hello,它会立即失败,因为它现在知道第一个字符必须是 a([- 它不是可选的。而在版本 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 相同。

于 2013-07-12T15:24:50.023 回答
1

如果您选择使用正则表达式,则必须描述用 . 分隔的所有可能性|。这是第一个范围的示例(您可以轻松地为其他范围添加值):

 @"1\.[5-9]|[23](?:\.[0-9])?|4"

但 IMO 最优雅的方法是提取所有数字并在之后将它们测试为数字。

于 2013-07-12T15:12:40.387 回答