it-swarm-ru.tech

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

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

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

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

69
Andrew Borley

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

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

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

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

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

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

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

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

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

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

72
Steve Jessop

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

38
Paweł Hajdan

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

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

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

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

11
Greg Hewgill

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

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

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

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

11
Andrew Borley

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

7
Duncan Smart

Мои 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        
}
6
Sudhanshu Mishra

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

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

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

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

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

4
nomuus

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

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

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

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

Наслаждаться!!!

4
Israel Romero

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

^(((\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, *
3
jrudolph
^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$

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

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

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

3
ofaurax

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

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

3
Shiva

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

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

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

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

2
rslite

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

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

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

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

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

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

  • группа 1: Major или Major.minor или '*'
  • группа 2, если существует: несовершеннолетняя или *
  • группа 3, если существует: *

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

2
VonC

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

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

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

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

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

2
Victor

Я считаю это хорошим упражнением 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;
}
2
vitaly-t

Еще одно решение:

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