代码之家  ›  专栏  ›  技术社区  ›  Andrew Borley

用于版本号分析的regex

  •  69
  • Andrew Borley  · 技术社区  · 17 年前

    我有以下表单的版本号:

    版本.发布.修改

    其中,版本、发布和修改是一组数字或通配符“*”。此外,这些数字中的任何一个(和前面的任何数字)都可能丢失。

    因此,以下内容是有效的,并分析为:

    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
    

    但这些都是无效的:

    *.12
    *123.1
    12*
    12.*.34
    

    有人能提供一个不太复杂的regex来验证和检索版本号、版本号和修改号吗?

    19 回复  |  直到 17 年前
        1
  •  66
  •   Alexander Taylor yunyi hu    11 年前

    我将格式表述为:

    “1-3个点分隔的组件,每个数字,最后一个除外*”

    作为regexp,这是:

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

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

    在我的解决方案中,组捕获 "." 字符。这可以用Ajborley的答案中的非捕获组来处理。

    另外,最右边的组将捕获最后一个组件,即使少于三个组件,例如,两个组件的输入导致第一个和最后一个组捕获,中间的一个未定义。我认为这可以由支持的非贪婪团体来处理。

    在regexp之后处理这两个问题的Perl代码可以是这样的:

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

    这并不比分拆短多少 “。” ]

        2
  •  38
  •   community wiki Paweł Hajdan    17 年前

    使用regex,现在有两个问题。 我将把它拆分为点(“.”),然后确保每个部分都是通配符或一组数字(regex现在是完美的)。如果事情是有效的,您只需返回正确的分割块。

        3
  •  11
  •   Greg Hewgill    17 年前

    这可能有效:

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

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

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

        4
  •  11
  •   Andrew Borley    17 年前

    感谢所有的回复!这是ACE:

    基于OneByone的回答(对我来说最简单),我添加了一些非捕获组(the“(?”:'部件-感谢VONC向我介绍非捕获组!),因此捕获的组只包含数字或*字符。

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

    非常感谢大家!

        5
  •  6
  •   Duncan Smart    17 年前

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

        6
  •  5
  •   svrist    17 年前

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

    我在Perl中为您的问题创建了一个“测试人员”。

    #!/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\n";
    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\n";
    
      }else{
      print " nomatch\n";
      }
      }
    print "------------------------\n";
    }
    

    电流输出:

    > 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
    ------------------------
    
        7
  •  5
  •   Sudhanshu Mishra    11 年前

    我的2分:我有这样的场景:我必须从字符串文本中解析版本号。 (我知道这与最初的问题非常不同,但是谷歌搜索来找到一个用于解析版本号的regex在顶部显示了这个线程,所以在这里添加这个答案)

    所以字符串文字应该是这样的:“服务版本1.2.35.564正在运行!”

    我必须从这个文本中分析1.2.35.564。根据@ajborley的提示,我的regex如下:

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

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

    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        
    }
    
        8
  •  4
  •   Emre Erkan    14 年前

    这应该符合你的规定。它取决于通配符位置,是一个嵌套的regex:

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

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

        9
  •  4
  •   community wiki 2 revs, 2 users 80% Israel Romero    8 年前

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

    01.0.0无效 1.0.0有效 10.0.10有效 1.0.0000无效

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

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

    享受!!!!

        10
  •  3
  •   jrudolph    17 年前

    另一种尝试:

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

    这给出了第4、5、6组中的三个部分,但: 它们右对齐。因此,4、5或6中的第一个非空字段给出了版本字段。

    • 1.2.3给出1、2、3
    • 1.2.*给出1,2,,*
    • 1.2给出空值,1,2
    • ***给出空,空,*
    • 1.*给出空值,1,,*
        11
  •  3
  •   Shiva    9 年前

    我需要搜索/匹配版本号,这遵循Maven约定,甚至只是一个数字。但在任何情况下都没有限定符。很奇怪,我花了不少时间才想到:

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

    这确保了版本,

    1. 以数字开头
    2. 可以有任何数字
    3. 只允许数字和“.”

    一个缺点是,版本甚至可以以“.”结尾,但它可以处理不定长度的版本(如果您想称之为疯狂的版本控制)

    比赛:

    • 1.2.3
    • 1.05.5
    • 3.4.4.5.7.8.
    • 23.6、209、23、4.3

    如果你不不满意“.”结尾,可以结合endswith逻辑

        12
  •  2
  •   VonC    17 年前
    (?ms)^((?:\d+(?!\.\*)\.)+)(\d+)?(\.\*)?$|^(\d+)\.\*$|^(\*|\d+)$
    

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

    • 第一组:大调或大调、小调或“*”
    • 第2组(如果存在):未成年人或*
    • 组3如果存在:*

    您可以删除'(?“女士”
    我用它来指示这个regexp在多行应用到 QuickRex

        13
  •  2
  •   Victor    17 年前

    这也符合1.2.3.*的要求。

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

    我建议不那么优雅的:

    (*\ d+(?\d+)?(*)?“d+.\d+.d+”

        14
  •  2
  •   FrankS    17 年前

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

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

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

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

        15
  •  2
  •   rslite    17 年前

    似乎很难让一个正则表达式完全满足您的需要(即只接受您需要的情况并拒绝 全部的 其他 返回三个组件的一些组)。我试了一下,想出来:

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

    IMO(我没有进行过广泛的测试)作为输入的验证器应该可以很好地工作,但是问题是这个regex没有提供一种检索组件的方法。为此,你仍然需要在周期上做一个分割。

    这个解决方案不是一体的,但在大多数情况下,在编程时不需要这样做。当然,这取决于代码中可能存在的其他限制。

        16
  •  2
  •   community wiki ofaurax    17 年前
    ^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$
    

    也许更简洁的一点是:

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

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

        17
  •  2
  •   Emmerson    9 年前

    指定XSD元素:

    <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>
    
        18
  •  2
  •   vitaly-t    9 年前

    我接受这个,作为一个很好的练习- vparse ,它有一个 tiny source ,功能简单:

    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;
    }
    
        19
  •  1
  •   Oleksandr Yarushevskyi    9 年前

    另一个解决方案:

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