Регулярное выражение для разбора номера версии


у меня есть номер версии следующего вида:

версия.освобождать.модификация

где 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

может ли кто-нибудь предоставить мне не слишком сложное регулярное выражение для проверки и получить номера выпуска, версии и модификации?

19 66

19 ответов:

Я бы выразил формат как:

" 1-3 компонента, разделенные точками, каждый числовой, за исключением того, что последний может быть *"

как регулярное выражение, это:

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

[Edit to add: это решение является кратким способом проверки, но было указано, что извлечение значений требует дополнительной работы. Это вопрос вкуса, следует ли иметь дело с этим, усложняя регулярное выражение, или путем обработки сопоставленных групп.

в моем решение, группы захвата "." символы. Это можно решить с помощью групп без захвата, как в ответе аджборли.

кроме того, самая правая группа будет захватывать последний компонент, даже если их меньше трех, и поэтому, например, двухкомпонентный ввод приводит к захвату первой и последней групп, а средняя не определена. Я думаю, что это можно решить с помощью не жадных групп, где поддерживается.

Perl код для решения обеих проблем после регулярное выражение может быть что-то вроде этого:

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

который на самом деле не короче, чем разделение на "." ]

использовать regex и теперь у вас есть две проблемы. Я бы разделил дело на точки ("."), затем убедитесь, что каждая часть является либо подстановочным знаком, либо набором цифр (регулярное выражение теперь идеально). Если вещь действительна, вы просто возвращаете правильный кусок раскола.

Это может сработать:

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

на верхнем уровне " * " является частным случаем допустимого номера версии. В противном случае он начинается с числа. То есть ноль, один или два ".nn "последовательности, за которыми следует необязательный".* " . Это регулярное выражение будет принимать 1.2.3.* которые могут быть разрешены или не разрешены в вашей заявке.

код для извлечения совпадающих последовательностей, особенно (\.\d+){0,2} часть, будет зависеть от вашей конкретной библиотеки регулярных выражений.

Спасибо за все ответы! Это туз :)

основываясь на ответе OneByOne (который выглядел самым простым для меня), я добавил некоторые группы без захвата (' (?: 'parts-спасибо VonC за то, что познакомил меня с группами без захвата!), поэтому группы, которые захватывают только цифры или символ*.

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

большое спасибо всем!

Не знаю, на какой платформе вы находитесь, но в .NET есть система.Класс версии, который будет анализировать номера версий" n.n.n.n " для вас.

Я склонен согласиться с разделением предложение.

Я создал "тестер" для вашей проблемы в 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 =  if ( &&  ne "*") ;
    $maj =  if ( &&  ne "*") ;
    $min =  if ( &&  ne "*") ;
    $rev =  if ( &&  ne "*") ;
    $ex1 =  if ( &&  ne "*") ;
    $ex2 =  if ( &&  ne "*") ;
    $ex3 =  if ( &&  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
------------------------

мои 2 цента: у меня был такой сценарий: я должен был разобрать номера версий из строкового литерала. (Я знаю, что это очень отличается от исходного вопроса, но поиск в Google, чтобы найти регулярное выражение для разбора номера версии, показал этот поток вверху, поэтому добавьте этот ответ здесь)

таким образом, строковый литерал будет выглядеть примерно так: "версия службы 1.2.35.564 запущена!"

мне пришлось разобрать 1.2.35.564 из этого литерала. Принимая сигнал от @ajborley, мое регулярное выражение таково следует:

(?:(\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        
}

Это должно работать для того, что вы оговорили. Он зависит от позиции wild card и является вложенным регулярным выражением:

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

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

Я видел много ответов, но... у меня есть новый. По крайней мере, для меня это работает. Я добавил новое ограничение. Номера версий не могут запускаться (основные, второстепенные или патч) с любыми нулями, за которыми следуют другие.

01.0.0 не действует 1.0.0 действует 10.0.10 действует 1.0.0000 не действует

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

Он основан на предыдущем. Но я вижу это решение лучше... для меня ;)

наслаждайтесь!!!

еще одну попытку:

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

Это дает три части в группах 4,5,6 но: Они выровнены справа. Таким образом, первый ненулевой один из 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 или даже просто одной цифрой. Но никакого классификатора ни в коем случае. Это было странно, мне потребовалось время, затем я придумал это:

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

Это гарантирует, версия,

  1. начинается с цифры
  2. может иметь любое количество цифр
  3. только цифры и '.- разрешены

один недостаток заключается в том, что версия может даже заканчиваться '.- Но он может обрабатывать неопределенную длину версии (сумасшедшая версия, если вы хотите это назвать)

матчи:

  • 1.2.3
  • 1.09.5
  • 3.4.4.5.7.8.8.
  • 23.6.209.234.3

Если вы не недовольна '."окончание, может быть, вы можете объединить с endswith логики

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

точно соответствует вашим 6 первым примерам и отклоняет 4 других

  • группа 1: майор или майор.минор или '*'
  • группа 2 если существует: незначительные или *
  • группа 3 Если существует: *

вы можете удалить '(?ms)'
Я использовал его, чтобы указать на это регулярное выражение, которое будет применяться на нескольких строках через QuickRex

Это соответствует 1.2.3.* слишком

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

Я бы предложил менее элегантно:

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

имейте в виду, что регулярное выражение жадно, поэтому, если вы просто ищете в строке номера версии, а не в большем тексте, используйте ^ и$, чтобы отметить начало и конец вашей строки. Регулярное выражение от Грега, кажется, работает нормально (просто дал ему быструю попытку в моем редакторе), но в зависимости от вашей библиотеки/языка первая часть все еще может соответствовать "*" в неправильных номерах версий. Может быть, я что-то упускаю, так как я не использовал Regexp в течение года или около того.

Это должно убедиться, что вы можете только найти правильные номера версий:

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

edit: на самом деле Грег уже добавил их и даже улучшил свое решение, я слишком медленный :)

Кажется довольно трудно иметь регулярное выражение, которое делает именно то, что вы хотите (т. е. принимать только те случаи, которые вам нужны, и отклонять все другие и возврат некоторых групп для трех компонентов). Я дал ему попробовать и придумать это:

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

IMO (я не тестировал экстенсивно) это должно работать нормально как валидатор для ввода, но проблема в том, что это регулярное выражение не предлагает способ извлечения компонентов. Для этого Вам еще нужно сделать разделить на период.

Это решение не является все-в-одном, но в большинстве случаев в программировании это не нужно. Конечно, это зависит от других ограничений, которые могут быть в коде.

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

возможно, более кратким можно было бы быть :

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

это может быть увеличено до 1.2.3.4.5.* или ограничено точно X. Y. Z с помощью * или {2} вместо {0,2}

указание элементов 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>

мой взгляд на это, как хорошее упражнение - vparse, которая имеет крошечный источник, С простой функцией:

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-9][\d]*(.[1-9][\d]*)*(.\*)?|\*$