Регулярное выражение для разбора номера версии - PullRequest
69 голосов
/ 17 сентября 2008

У меня есть номер версии следующей формы:

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 ]

71 голосов
/ 17 сентября 2008

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

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

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

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

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

В моем решении группы захватывают символы ".". С этим можно справиться, используя группы без захвата, как в ответе Айборли.

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

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

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

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

38 голосов
/ 17 сентября 2008

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

11 голосов
/ 17 сентября 2008

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

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

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

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

11 голосов
/ 17 сентября 2008

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

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

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

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

7 голосов
/ 17 сентября 2008

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

6 голосов
/ 18 декабря 2014

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

Таким образом, строковый литерал будет выглядеть примерно так: «Сервисная версия 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        
}
5 голосов
/ 17 сентября 2008

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

Я создал "тестер" для вашей проблемы в 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
------------------------
4 голосов
/ 12 апреля 2010

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

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

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

4 голосов
/ 14 мая 2013

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

01.0.0 недействительно 1.0.0 действителен 10.0.10 действует 1.0.0000 недействительно

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

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

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

3 голосов
/ 12 июня 2017

У меня было требование искать / сопоставлять номера версий, которые соответствуют условию 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

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

...