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

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

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

Я бы выделил формат:

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

Как regexp, это:

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

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

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

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

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

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

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

Ответ 2

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

Ответ 6

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

Ive создал "тестер" для вашей проблемы в 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

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

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

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

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

Небольшой фрагмент С# для проверки этого выглядит следующим образом:

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

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

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

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

Ответ 9

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

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

Еще одна попытка:

^(((\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

У меня было требование для поиска/сопоставления номеров версий, которые следует за соглашением maven или даже с одной цифрой. Но никакого квалификатора в любом случае. Это было странно, мне потребовалось время, я придумал это:

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

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

  • Начинается с цифры
  • Может иметь любое количество цифр
  • Только цифры и '.' разрешены

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

Матчи:

  • 1.2.3
  • 1.09.5
  • 3.4.4.5.7.8.8.
  • 23.6.209.234.3

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

Ответ 12

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

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

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

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

Ответ 13

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

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

  • группа 1: майор или майор .minor или '*'
  • группа 2, если существует: minor или *
  • группа 3, если существует: *

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

Ответ 14

Это соответствует 1.2.3. * too

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

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

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

Ответ 15

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

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

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

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

Ответ 16

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

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

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

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

Ответ 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, который имеет крошечный источник, с простой функцией:

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

Ответ 20

Если вы используете Semantic versioning здесь, регулярное выражение, которое устанавливает tat spec (используемый PCRE для названных групп, не стесняйтесь удалять <groupnames> и добавлять ^ и $ для соответствия началу и концу sting:

(?<version_core>(?<major>(?:[0-9]|[1-9][0-9]+))\.(?<minor>(?:[0-9]|[1-9][0-9]+))\.(?<patch>(?:[0-9]|[1-9][0-9]+)))(?:-(?<pre_release>(?:0|[1-9A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9A-Za-z-][0-9A-Za-z-]*))*))?(?:\+(?<build>[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?

Regular expression visualization

Демоверсия Debuggex