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

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

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

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

Я не уверен, что «простой» возможен.

svrist 17.09.2008 15:11
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
88
1
131 115
22
Перейти к ответу Данный вопрос помечен как решенный

Ответы 22

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

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

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

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

Отличный ответ! Я думаю, вам следует заменить неэкранированный * на {0,2}, чтобы предотвратить соответствие 1.2.3.4. В зависимости от вашей библиотеки регулярных выражений вы можете заключить шаблон в ^ (<pattern>) $, если вы можете выполнять только поиск, а не совпадение.

Dave Webb 17.09.2008 15:29

Небольшое изменение в ^ (* | \ d + (\. \ D +) {0,1} (?: (\. *)? | (\. \ D +)?)) $ Также сделало бы 1.2.3. * Недействительным

Pieter 17.09.2008 15:47

Питер: Думаю, я сейчас остановлюсь на своем. Это быстро переходит на территорию «теперь у вас две проблемы». :)

Greg Hewgill 17.09.2008 15:49

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

Ответ принят как подходящий

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

"1-3 dot-separated components, each numeric except that the last one may be *"

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

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

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

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

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

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

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

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

Добавление некоторых групп без захвата (см. Мой ответ ниже) означает, что группы захвата не захватывают конечные '.' ^ (?: (\ d +) \.)? (?: (\ d +) \.)? (* | \ d +) $ Спасибо!

Andrew Borley 17.09.2008 16:15

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

jrudolph 17.09.2008 16:16

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

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

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

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

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

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

Это было бы неплохо, поскольку OneByOne выглядит самым простым.

jrudolph 17.09.2008 16:04

Вам также следует проверить неправильные. Вы пропустили процитировать точки OneByOne.

jrudolph 17.09.2008 16:11

Обновлено с точками и другими регулярными выражениями

svrist 17.09.2008 16:24

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

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

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

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

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

Нет, там с версии 1.0

Duncan Smart 27.10.2010 20:41

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

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

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

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

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

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

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

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

Не могли бы вы вместо этого добавить это как правку к вашему вопросу? Таким образом, правильные ответы будут ближе к началу

svrist 17.09.2008 16:25

С именами групп: ^ (? :(? <major> \ d +) \.)? (? :(? <minor> \ d +) \.)? (? <build> * | \ d +) $

javacavaj 20.05.2014 01:16

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

Sam 11.07.2016 10:34

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

Piotr Dobrogost 20.03.2019 14:28

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

^(((\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. * дает null, 1, *

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

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

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

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

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

Возможно, более лаконичным было бы:

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

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

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

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

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

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

01.0.0 is not valid 1.0.0 is valid 10.0.10 is valid 1.0.0000 is not valid

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

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

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

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

Я знаю, что вы описываете альтернативную ситуацию и случай, но для полноты: SemVer 'требует', чтобы строка версии имела формат X.Y.Z (то есть ровно три части), где X и Y должны быть неотрицательными целыми числами и без дополнительных ведущих нулей. См. semver.org.

Jochem Schulenklopper 14.03.2017 16:53

@JochemSchulenklopper спасибо, я знаю о SemVer, хотя в вопросе ничего не упоминается о SemVer.

Sudhanshu Mishra 15.03.2017 01:45

Истинный. Меня направил на этот вопрос коллега о синтаксическом анализе строк SemVer, так что я прочитал ответы на эти вопросы.

Jochem Schulenklopper 15.03.2017 12:25

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

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

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

У меня было требование искать / сопоставлять номера версий в соответствии с соглашением 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 26.01.2018 16:50

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

Это помогло мне.

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

Допустимые случаи:

1, 0.1, 1.2.1

Иногда номера версий могут содержать второстепенную буквенно-цифровую информацию (например, 1.2.0b или 1.2.0-бета). В этом случае я использую это регулярное выражение:

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

Я нашел это, и у меня это работает:

/(\^|\~?)(\d|x|\*)+\.(\d|x|\*)+\.(\d|x|\*)+

Другие вопросы по теме