Регулярний вираз для розбору номера версії


84

У мене є номер версії в такій формі:

версія.випуск.модифікація

де версія, випуск і модифікація - це або набір цифр, або символ підстановки '*'. Крім того, будь-який із цих номерів (і будь-який попередній.) Може бути відсутнім.

Отже, наступне є дійсним і розбирається як:

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

Хтось може надати мені не надто складний регулярний вираз для перевірки та отримання номерів випуску, версії та модифікації?


Я не впевнений, що "простий" можливий.
svrist

Відповіді:


96

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

"1-3 розділені крапками компоненти, кожен числовий, за винятком того, що останній може бути *"

Як регулярний вираз, це:

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

[Редагувати, щоб додати: це рішення є стислим способом перевірки, але було зазначено, що для вилучення значень потрібна додаткова робота. Це справа смаку, чи боротися з цим, ускладнюючи регулярний вираз, або обробляючи відповідні групи.

За моїм рішенням, групи захоплюють "."персонажів. З цим можна впоратися із використанням груп, що не захоплюють, як у відповіді Айборлі.

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

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

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

Що насправді не коротше, ніж розбиття на "." ]


1
Додавання деяких груп, які не захоплюють (див. Мою відповідь нижче), означає, що групи захоплення не фіксують кінцевий '.' ^ (?: (\ d +) \.)? (?: (\ d +) \.)? (* | \ d +) $ Дякую!
Ендрю Борлі,

Єдина проблема з цією - дуже приємною та чистою пропозицією - полягає в тому, що групи не мають рації, оскільки 1.2 захопить 1 у першій та 2 у третій групі через жадібність.
jrudolph

39

Використовуйте регулярний вираз, і тепер у вас дві проблеми. Я б розділив цю річ на крапки ("."), А потім переконався, що кожна частина є або символом підстановки, або набором цифр (регулярний вираз зараз ідеальний). Якщо річ дійсна, ви просто повертаєте правильний шматок розділення.


11

Це може спрацювати:

^(\*|\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. * Теж
Пітер

2
Пітер: Я думаю, що я зупинюсь там, де я зараз є. Це швидко потрапляє на територію "тепер у вас дві проблеми". :)
Грег Хьюгілл,

11

Дякуємо за всі відповіді! Це туз :)

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

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

Всім велике спасибі!


1
Чи не могли б ви додати це як редагування до свого запитання? Таким чином правильні відповіді близькі до верху
svrist

1
З іменами груп: ^ (? :(? <major> \ d +) \.)? (?
:(

1
підтримка напівверсії (трохи більше). - "1.2.3-alpha + abcdedf.lalal" -match "^ (?: (\ D +) \.)? (?: (\ D +) \.)? (* | \ D +)? (?: \ - ([[A-Za-z0-9 \.] +))? (?: \ + ([A-Za-z0-9 \.] +))? $ "
Сем,

Пам'ятайте, що у випадку версії, що складається з одного числа, вона буде збігатися з третьою, а (\*|\d+)не першою ^(?:(\d+)\.)?групою.
Piotr

8

Мої 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        
}

Я знаю, що ви описуєте альтернативну ситуацію та випадок, але лише для того, щоб бути повним: SemVer "вимагає", щоб рядок версії мав формат X.Y.Z(тобто, рівно три частини), де X та Y повинні бути невід’ємними цілими числами, додаткові провідні нулі. Див. Semver.org .
Йохем Шуленклоппер

1
@JochemSchulenklopper дякую, я знаю про SemVer, хоча в питанні нічого не згадується про SemVer.
Судханшу Мішра,

1
Правда. На це запитання мене звернувся колега щодо синтаксичного аналізу рядків SemVer, так що це спроектувало моє читання відповідей.
Jochem Schulenklopper

7

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


Ні, це було з версії 1.0
Duncan Smart

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 виглядає як найпростіший.
jrudolph

Слід перевірити і неправильні. Ви пропустили цитати крапок OneByOne.
jrudolph

Оновлено крапками та іншими регулярними
виразами

4

Це має працювати на те, що ви передбачили. Він залежить від позиції підстановки та є вкладеним регулярним виразом:

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

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


4

Я бачив багато відповідей, але ... у мене є нова. Це принаймні для мене працює. Я додав нове обмеження. Номери версій не можуть починатися (мажор, мінор або патч) з будь-яких нулів, за якими слід інші.

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

Ще одна спроба:

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

Це дає три частини в групах 4,5,6 АЛЕ: Вони вирівняні праворуч. Отже, перший ненульовий із 4,5 або 6 дає поле версії.

  • 1.2.3 дає 1,2,3
  • 1.2. * Дає 1,2, *
  • 1.2 дає нуль, 1,2
  • *** дає null, null, *
  • 1. * дає нуль, 1, *

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

Можливо, більш стислим може бути:

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

Потім це можна підвищити до 1.2.3.4.5. * Або обмежити саме XYZ, використовуючи * або {2} замість {0,2}


3

У мене була вимога шукати / зіставляти номери версій, що відповідає умовам 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

Якщо вас не влаштовує "." закінчення, можливо, ви можете поєднувати з кінцямиз логікою


Щоб позбутися останньої цифри, можливо, ви хотіли б спробувати це:(\d+)(.\d+)*
cassioso

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

Точно відповідає вашим 6 першим прикладам і відхиляє 4 інших

  • група 1: мажор або мажор. мінор або "*"
  • група 2, якщо існує: неповнолітній або *
  • група 3, якщо існує: *

Ви можете видалити '(? Ms)'
Я використав його для позначення цього регулярного виразу, який буде застосовано до багаторядкових рядків через QuickRex


2

Це також відповідає 1.2.3. *

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

Я б запропонував менш елегантне:

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


2

Майте на увазі, регулярний вираз є жадібним, тому, якщо ви просто шукаєте в рядку номера версії, а не в більшому тексті, використовуйте ^ та $, щоб позначити початок і кінець вашого рядка. Здається, регулярний вираз від Greg працює нормально (просто спробував у моєму редакторі), але залежно від вашої бібліотеки / мови перша частина все одно може відповідати знаку "*" в неправильних номерах версій. Можливо, мені чогось не вистачає, оскільки я не користувався Regexp близько року.

Це має переконатися, що ви можете знайти лише правильні номери версій:

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

редагувати: насправді greg їх вже додав і навіть вдосконалив своє рішення, я занадто повільний :)


2

Здається, складно мати регулярний вираз, який робить саме те, що ви хочете (тобто приймайте лише ті випадки, які вам потрібні, і відхиляйте всі інші, а також повертайте деякі групи для трьох компонентів). Я спробував і придумав таке:

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

IMO (я не тестував широко), це повинно добре працювати як валідатор для вхідних даних, але проблема в тому, що цей регулярний вираз не пропонує спосіб отримання компонентів. Для цього вам все-таки доведеться зробити поділ на період.

Це рішення не є єдиним, але в більшості випадків у програмуванні це не потрібно. Звичайно, це залежить від інших обмежень, які ви можете мати у своєму коді.


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>

2

Я вважаю це хорошим вправою - 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

Для аналізу номерів версій, які відповідають цим правилам: - Є лише цифри та крапки - Не можна починати або закінчувати крапкою - Не може бути двома крапками разом

Цей зробив мені фокус.

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

Дійсні випадки:

1, 0,1, 1,2,1



1

Іноді номери версій можуть містити буквено-цифрові другорядні відомості (наприклад, 1.2.0b або 1.2.0-beta ). У цьому випадку я використовую цей регулярний вираз:

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.