设置表示法样式范围验证器的正则表达式

时间:2013-07-12 14:59:51

标签: c# .net regex

我正在实现一个多范围验证器,它可以同时验证多个范围的用户输入。

示例

如果我定义范围[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。

2 个答案:

答案 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最优雅的方式是提取所有数字并将其作为数字进行测试。