版本号解析的正则表达式

Posted

技术标签:

【中文标题】版本号解析的正则表达式【英文标题】:A regex for version number parsing 【发布时间】:2010-09-10 01:42:14 【问题描述】:

我有一个如下形式的版本号:

version.release.modification

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

所以以下是有效的并解析为:

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

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

【问题讨论】:

我不确定“简单”是否可行。 挑战:任何人都能够管理出一个结合以下所有答案的表达式? :) 【参考方案1】:

这可能有效:

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

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

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

【讨论】:

很好的答案!我认为您应该将未转义的 * 交换为 0,2 以防止 1.2.3.4 匹配。根据您的正则表达式库,如果您只能进行搜索而不是匹配,您可能希望将模式包含在 ^()$ 中。 对 ^(*|\d+(\.\d+)0,1(?:(\.*)?|(\.\d+)?))$ 的轻微改动将使1.2.3.* 也是 Pieter:我想我现在要停下来了。这很快就进入了“现在你有两个问题”的领域。 :)【参考方案2】:

使用正则表达式,现在你有两个问题。我会用点(“.”)分割事物,然后确保每个部分是通配符或数字集(正则表达式是完美的现在)。如果事情是有效的,你只需返回正确的拆分块。

【讨论】:

【参考方案3】:

我将格式表示为:

"1-3个点分隔的组件,每个数字除了最后一个可能是*"

作为一个正则表达式,即:

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

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

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

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

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

@version = ();
@groups = ($1, $2, $3);
foreach (@groups) 
    next if !defined;
    s/\.//;
    push @version, $_;

($major, $minor, $mod) = (@version, "*", "*");

这并不比在"." 上拆分更短 ]

【讨论】:

添加一些非捕获组(见下面我的回答)意味着捕获组不捕获尾随的 '.' ^(?:(\d+)\.)?(?:(\d+)\.)?(*|\d+)$ 谢谢! 唯一的问题 - 这是一个非常好的和干净的提案 - 组是不正确的,因为 1.2 会因为贪婪而在第一组中捕获 1 和在第三组中捕获 2。跨度> 【参考方案4】:

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

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

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

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

【讨论】:

【参考方案5】:

我倾向于同意拆分建议。

我已经在 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
------------------------

【讨论】:

那很好,因为 OneByOne 看起来是最直接的。 你也应该测试错误的。你错过了引用 OneByOne 的点。 更新了点和更多正则表达式【参考方案6】:
(?ms)^((?:\d+(?!\.\*)\.)+)(\d+)?(\.\*)?$|^(\d+)\.\*$|^(\*|\d+)$

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

第 1 组:major 或 major.minor 或 '*' 第 2 组(如果存在):次要或 * 第 3 组(如果存在):*

您可以删除“(?ms)” 我用它来指示这个正则表达式通过QuickRex应用于多行

【讨论】:

【参考方案7】:

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

【讨论】:

不,它从 1.0 版开始就存在【参考方案8】:

这也匹配 1.2.3.*

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

我会建议不太优雅的:

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

【讨论】:

【参考方案9】:

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

根据 OneByOne 的回答(这对我来说看起来最简单),我添加了一些非捕获组('(?:' 部分 - 感谢 VonC 将我介绍给非捕获组!),所以这些组捕获仅包含数字或 * 字符。

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

非常感谢大家!

【讨论】:

您可以将此作为编辑添加到您的问题中吗?这样,正确的答案就接近顶部 使用组名:^(?:(?\d+)\.)?(?:(? \d+)\.)?(?*| \d+)$ 支持 semversion(多一点)。 - "1.2.3-alpha+abcddf.lalal" - 匹配 "^(?:(\d+)\.)?(?:(\d+)\.)?(*|\d+)?(?:\- ([A-Za-z0-9\.]+))?(?:\+([A-Za-z0-9\.]+))?$" 请注意,如果版本由单个数字组成,它将与第三个 (\*|\d+) 而不是第一个 ^(?:(\d+)\.)? 组匹配。【参考方案10】:

再试一次:

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

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

1.2.3 给出 1,2,3 1.2.* 给出 1,2,* 1.2 给出 null,1,2 *** 给出 null,null,* 1.* 给出 null,1,*

【讨论】:

【参考方案11】:

似乎很难有一个正则表达式完全符合您的要求(即只接受您需要的情况并拒绝所有其他情况返回一些组三个组成部分)。我已经尝试过并想出了这个:

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

IMO(我没有进行过广泛的测试)这应该可以很好地作为输入的验证器,但问题是这个正则表达式不提供检索组件的方法。为此,您仍然需要对期间进行拆分。

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

【讨论】:

【参考方案12】:
^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$

也许更简洁的一个可能是:

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

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

【讨论】:

【参考方案13】:

这应该适用于您的规定。它取决于通配符的位置,是一个嵌套的正则表达式:

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

【讨论】:

【参考方案14】:

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

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

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

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

享受!!!

【讨论】:

在 js 中不工作【参考方案15】:

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

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

我不得不从这个文字中解析出 1.2.35.564。从@ajborley 得到启发,我的正则表达式如下:

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

一个用于测试的小型 C# sn-p 如下所示:

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        

【讨论】:

我知道您在描述另一种情况和案例,但只是为了完整:SemVer“要求”版本字符串的格式为X.Y.Z(因此,正好是三个部分),其中X 和 Y 必须是非负整数并且没有额外的前导零。见semver.org。 @JochemSchulenklopper 谢谢,我知道 SemVer,虽然问题没有提到任何关于 SemVer 的内容。 是的。一位同事向我介绍了这个关于解析 SemVer 字符串的问题,因此构成了我对答案的阅读框架。【参考方案16】:

另一种解决方案:

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

【讨论】:

【参考方案17】:

指定 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】:

我对此的看法,作为一个很好的练习 - 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】:

我需要搜索/匹配版本号,这遵循 maven 约定,甚至只是单个数字。但无论如何都没有限定符。这很奇怪,我花了一些时间才想出这个:

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

这样可以确保版本,

    以数字开头 可以有任意位数 只有数字和'.'被允许

一个缺点是版本甚至可以以“.”结尾。但它可以处理无限长度的版本(如果你想这样称呼它,那就是疯狂的版本控制)

匹配:

1.2.3 1.09.5 3.4.4.5.7.8.8. 23.6.209.234.3

如果您对 '.' 不满意结束,也许你可以结合用逻辑结束

【讨论】:

为了去掉最后一个数字,也许你想试试这个:(\d+)(.\d+)*【参考方案20】:

用于解析遵循以下规则的版本号: - 只有数字和点 - 不能以点开头或结尾 - 不能是两个点

这个对我有用。

^(\d+)((\.1\d+)*)(\.0)$

有效的情况是:

1、0.1、1.2.1

【讨论】:

【参考方案21】:

有时版本号可能包含字母数字次要信息(例如 1.2.0b1.2.0-beta)。在这种情况下,我正在使用这个正则表达式:

([0-9]1,4(\.[0-9a-z]1,6)1,5)

【讨论】:

【参考方案22】:

我找到了这个,它对我有用:

/(\^|\~?)(\d|x|\*)+\.(\d|x|\*)+\.(\d|x|\*)+

【讨论】:

【参考方案23】:
/^([1-9]1\d0,3)(\.)([0-9]|[1-9]\d1,3)(\.)([0-9]|[1-9]\d1,3)(\-(alpha|beta|rc|HP|CP|SP|hp|cp|sp)[1-9]\d*)?(\.C[0-9a-zA-Z]+(-U[1-9]\d*)?)?(\.[0-9a-zA-Z]+)?$/
普通版:([1-9]1\d0,3)(\.)([0-9]|[1-9]\d1,3)(\.)([0-9]|[1-9]\d1,3) 预发布或修补版本:(\-(alpha|beta|rc|EP|HP|CP|SP|ep|hp|cp|sp)[1-9]\d*)?(扩展包、修补程序包、Coolfix 包、服务包) 定制版:(\.C[0-9a-zA-Z]+(-U[1-9]\d*)?)? 内部版本:(\.[0-9a-zA-Z]+)?

【讨论】:

以上是关于版本号解析的正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

大于 1.18.10 的版本号的正则表达式 [重复]

bash 正则表达式匹配语义版本号

版本号的正则表达式

版本号的正则表达式

使用MSBuild用正则表达式更新版本号

Scala正则和抽取器:解析方法参数