关于版本控制:用于版本号解析的正则表达式

A regex for version number parsing

我有以下形式的版本号:

version.release.modification

其中版本,发行和修改是一组数字或" *"通配符。 此外,任何这些数字(和任何前面的。)可能会丢失。

因此,以下内容有效并解析为:

1
2
3
4
5
6
1.23.456 = version 1, release 23, modification 456
1.23     = version 1, release 23, any modification
1.23.*   = version 1, release 23, any modification
1.*      = version 1, any release, any modification
1        = version 1, any release, any modification
*        = any version, any release, any modification

但是这些是无效的:

1
2
3
4
*.12
*123.1
12*
12.*.34

谁能为我提供一个不太复杂的正则表达式来验证和检索版本号,版本号和修改号?


我将格式表示为:

"1-3 dot-separated components, each numeric except that the last one may be *"

作为正则表达式,那就是:

1
^(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)$

[编辑添加:此解决方案是一种简洁的验证方法,但已指出,提取值需要额外的工作。是否通过使正则表达式复杂化或处理匹配的组来解决这个问题。

在我的解决方案中,组捕获"."字符。可以像使用ajborley的答案那样使用非捕获组来解决。

同样,即使少于三个组件,最右边的组也将捕获最后一个组件,因此例如,两分量输入会导致捕获第一个和最后一个组而中间的一个未定义。我认为可以通过支持的非贪婪团体来解决。

在正则表达式后处理这两个问题的Perl代码可能是这样的:

1
2
3
4
5
6
7
8
@version = ();
@groups = ($1, $2, $3);
foreach (@groups) {
    next if !defined;
    s/\\.//;
    push @version, $_;
}
($major, $minor, $mod) = (@version,"*","*");

这实际上并不比在"."上分割要短
]


使用正则表达式,现在有两个问题。我将东西分割为点("。"),然后确保每个部分都是通配符或数字集(正则表达式现在很完美)。如果事物是??有效的,则只需返回拆分的正确块。


感谢您的所有回复!这是王牌:)

根据OneByOne的回答(对我而言,这似乎是最简单的),我添加了一些非捕获组("(?:"部分-感谢VonC向我介绍了非捕获组!),因此仅捕获了这些组包含数字或*字符。

1
^(?:(\\d+)\\.)?(?:(\\d+)\\.)?(\\*|\\d+)$

非常感谢大家!


这可能起作用:

1
^(\\*|\\d+(\\.\\d+){0,2}(\\.\\*)?)$

在顶层," *"是有效版本号的特殊情况。否则,它以数字开头。然后有零个,一个或两个" .nn"序列,后跟一个可选的"。*"。此正则表达式将接受1.2.3。*,这可能会或可能不会在您的应用程序中允许。

检索匹配序列(尤其是(\\.\\d+){0,2}部分)的代码将取决于您的特定正则表达式库。


我的2分钱:我遇到了这种情况:我不得不从字符串文字中解析出版本号。
(我知道这与原始问题有很大不同,但是在谷歌搜索找到用于解析版本号的正则表达式在顶部显示了此线程,因此请在此处添加此答案)

因此,字符串文字将类似于:"服务版本1.2.35.564正在运行!"

我不得不从这个字面上解析出1.2.35.564。以@ajborley为线索,我的正则表达式如下:

1
(?:(\\d+)\\.)?(?:(\\d+)\\.)?(?:(\\d+)\\.\\d+)

一个小的C#代码片段测试如下:

1
2
3
4
5
6
7
void Main()
{
    Regex regEx = new Regex(@"(?:(\\d+)\\.)?(?:(\\d+)\\.)?(?:(\\d+)\\.\\d+)", RegexOptions.Compiled);

    Match version = regEx.Match("The Service SuperService 2.1.309.0) is Running!");
    version.Value.Dump("Version using RegEx");   // Prints 2.1.309.0        
}

不知道您在哪个平台上,但是.NET中有System.Version类,它将为您解析" n.n.n.n"版本号。


我倾向于同意分开的建议。

Ive为您在Perl中的问题创建了一个"测试器"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/bin/perl -w


@strings = ("1.2.3","1.2.*","1.*","*" );

%regexp = ( svrist => qr/(?:(\\d+)\\.(\\d+)\\.(\\d+)|(\\d+)\\.(\\d+)|(\\d+))?(?:\\.\\*)?/,
            onebyone => qr/^(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)$/,
            greg => qr/^(\\*|\\d+(\\.\\d+){0,2}(\\.\\*)?)$/,
            vonc => qr/^((?:\\d+(?!\\.\\*)\\.)+)(\\d+)?(\\.\\*)?$|^(\\d+)\\.\\*$|^(\\*|\\d+)$/,
            ajb => qr/^(?:(\\d+)\\.)?(?:(\\d+)\\.)?(\\*|\\d+)$/,
            jrudolph => qr/^(((\\d+)\\.)?(\\d+)\\.)?(\\d+|\\*)$/
          );

  foreach my $r (keys %regexp){
    my $reg = $regexp{$r};
    print"Using $r regexp\
";
foreach my $s (@strings){
  print"$s :";

    if ($s =~m/$reg/){
    my ($main, $maj, $min,$rev,$ex1,$ex2,$ex3) = ("any","any","any","any","any","any","any");
    $main = $1 if ($1 && $1 ne"*") ;
    $maj = $2 if ($2 && $2 ne"*") ;
    $min = $3 if ($3 && $3 ne"*") ;
    $rev = $4 if ($4 && $4 ne"*") ;
    $ex1 = $5 if ($5 && $5 ne"*") ;
    $ex2 = $6 if ($6 && $6 ne"*") ;
    $ex3 = $7 if ($7 && $7 ne"*") ;
    print"$main $maj $min $rev $ex1 $ex2 $ex3\
";

  }else{
  print" nomatch\
";
  }
  }
print"------------------------\
";
}

电流输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
> perl regex.pl
Using onebyone regexp
1.2.3 : 1. 2. 3 any any any any
1.2.* : 1. 2. any any any any any
1.* : 1. any any any any any any
* : any any any any any any any
------------------------
Using svrist regexp
1.2.3 : 1 2 3 any any any any
1.2.* : any any any 1 2 any any
1.* : any any any any any 1 any
* : any any any any any any any
------------------------
Using vonc regexp
1.2.3 : 1.2. 3 any any any any any
1.2.* : 1. 2 .* any any any any
1.* : any any any 1 any any any
* : any any any any any any any
------------------------
Using ajb regexp
1.2.3 : 1 2 3 any any any any
1.2.* : 1 2 any any any any any
1.* : 1 any any any any any any
* : any any any any any any any
------------------------
Using jrudolph regexp
1.2.3 : 1.2. 1. 1 2 3 any any
1.2.* : 1.2. 1. 1 2 any any any
1.* : 1. any any 1 any any any
* : any any any any any any any
------------------------
Using greg regexp
1.2.3 : 1.2.3 .3 any any any any any
1.2.* : 1.2.* .2 .* any any any any
1.* : 1.* any .* any any any any
* : any any any any any any any
------------------------

我看到了很多答案,但是...我有一个新答案。至少对我有用。我添加了一个新的限制。版本号不能以任何零开头(主要,次要或补丁),后跟其他零。

01.0.0 is not valid
1.0.0 is valid
10.0.10 is valid
1.0.0000 is not valid

1
^(?:(0\\\\.|([1-9]+\\\\d*)\\\\.))+(?:(0\\\\.|([1-9]+\\\\d*)\\\\.))+((0|([1-9]+\\\\d*)))$

它基于上一个。但是我觉得这个解决方案更好...对我来说;)

请享用!!!


这应该符合您的规定。它取决于通配符位置,并且是嵌套的正则表达式:

1
^((\\*)|([0-9]+(\\.((\\*)|([0-9]+(\\.((\\*)|([0-9]+)))?)))?))$

http://imgur.com/3E492.webp


另一种尝试:

1
^(((\\d+)\\.)?(\\d+)\\.)?(\\d+|\\*)$

这给出了组4,5,6 BUT中的三个部分:
它们向右对齐。因此,4,5或6中的第一个非空值将给出version字段。

  • 1.2.3给出1,2,3
  • 1.2。*给出1,2,*
  • 1.2给出null,1,2
  • ***给出null,null,*
  • 1. *为null,1,*

我有一个搜索/匹配版本号的要求,该版本号遵循maven约定,甚至只是个位数。但是无论如何都没有预选赛。这很奇怪,花了我一些时间,然后我想到了:

1
'^[0-9][0-9.]*$'

这样可以确保版本,

  • 以数字开头
  • 可以有任意位数
  • 仅数字和"。"被允许
  • 一个缺点是版本甚至可以以'。'结尾。但是它可以处理不确定的版本长度(如果您要称呼它为疯狂版本)

    火柴:

    • 1.2.3
    • 1.09.5
    • 3.4.4.5.7.8.8。
    • 23.6.209.234.3

    如果您对"。"不满意结尾,也许可以与逻辑结合


    1
    ^(?:(\\d+)\\.)?(?:(\\d+)\\.)?(\\*|\\d+)$

    也许更简洁一些:

    1
    ^(?:(\\d+)\\.){0,2}(\\*|\\d+)$

    然后可以将其增强为1.2.3.4.5。*或使用*或{2}而不是{0,2}严格限制为X.Y.Z


    这也匹配1.2.3。*

    ^(*|\\d+(.\\d+){0,2}(.*)?)$

    我会提出不太优雅的建议:

    (* | \ d +(。\ d +)?(。*)?)| \ d +。\ d +。\ d +)


    1
    (?ms)^((?:\\d+(?!\\.\\*)\\.)+)(\\d+)?(\\.\\*)?$|^(\\d+)\\.\\*$|^(\\*|\\d+)$

    与您的前6个示例完全匹配,并拒绝其他4个示例

    • 第1组:major或major.minor或'*'
    • 第2组(如果存在):未成年人或*
    • 第3组(如果存在):*

    您可以删除'(?ms)'
    我用它来表示此正则表达式将通过QuickRex应用于多行


    请记住,regexp是贪婪的,因此,如果您仅在版本号字符串中而不是在较大的文本中进行搜索,请使用^和$标记字符串的开始和结束。
    来自Greg的regexp似乎工作正常(只是在我的编辑器中进行了快速尝试),但是根据您的库/语言,第一部分仍可以在错误的版本号中匹配" *"。也许我错过了一些东西,因为我已经有大约一年没有使用Regexp了。

    这应该确保您只能找到正确的版本号:

    ^(\ * | \ d +(\。\ d +)*(\。\ *)?)$

    编辑:实际上greg已经添加了它们,甚至改进了他的解决方案,我太慢了:)


    指定XSD元素:

    1
    2
    3
    4
    5
    <xs:simpleType>
        <xs:restriction base="xs:string">
            <xs:pattern value="[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}(\\..*)?"/>
        </xs:restriction>
    </xs:simpleType>

    我认为这是一项很好的练习-vparse,它的来源很小,但功能简单:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function parseVersion(v) {
        var m = v.match(/\\d*\\.|\\d+/g) || [];
        v = {
            major: +m[0] || 0,
            minor: +m[1] || 0,
            patch: +m[2] || 0,
            build: +m[3] || 0
        };
        v.isEmpty = !v.major && !v.minor && !v.patch && !v.build;
        v.parsed = [v.major, v.minor, v.patch, v.build];
        v.text = v.parsed.join('.');
        return v;
    }

    正则表达式似乎很难满足您的要求(即仅接受您需要的情况,拒绝所有其他情况,并为这三个组成部分返回一些组),这似乎非常困难。我尝试一下,并提出以下建议:

    1
    ^(\\*|(\\d+(\\.(\\d+(\\.(\\d+|\\*))?|\\*))?))$

    IMO(我尚未进行广泛的测试),它可以很好地用作输入的验证器,但是问题是此正则表达式无法提供检索组件的方法。为此,您仍然必须进行分割。

    这种解决方案不是一站式的,但是在编程中大多数时候并不需要。当然,这取决于您代码中可能存在的其他限制。


    另一种解决方案:

    1
    ^[1-9][\\d]*(.[1-9][\\d]*)*(.\\*)?|\\*$


    推荐阅读