У меня есть номер версии следующего вида:
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
Может ли кто-нибудь предоставить мне не слишком сложное регулярное выражение для проверки и получения номеров выпуска, версии и модификации?





Это может сработать:
^(\*|\d+(\.\d+){0,2}(\.\*)?)$
На верхнем уровне «*» - это особый случай допустимого номера версии. В противном случае он начинается с числа. Затем идет ноль, одна или две последовательности «.nn», за которыми следует необязательный «. *». Это регулярное выражение будет принимать 1.2.3. *, Что может быть разрешено или не разрешено в вашем приложении.
Код для получения совпадающих последовательностей, особенно части (\.\d+){0,2}, будет зависеть от вашей конкретной библиотеки регулярных выражений.
Отличный ответ! Я думаю, вам следует заменить неэкранированный * на {0,2}, чтобы предотвратить соответствие 1.2.3.4. В зависимости от вашей библиотеки регулярных выражений вы можете заключить шаблон в ^ (<pattern>) $, если вы можете выполнять только поиск, а не совпадение.
Небольшое изменение в ^ (* | \ d + (\. \ D +) {0,1} (?: (\. *)? | (\. \ D +)?)) $ Также сделало бы 1.2.3. * Недействительным
Питер: Думаю, я сейчас остановлюсь на своем. Это быстро переходит на территорию «теперь у вас две проблемы». :)
Используйте регулярное выражение, и теперь у вас есть две проблемы. Я бы разделил это на точки ("."), А затем убедился, что каждая часть является либо подстановочным знаком, либо набором цифр (регулярное выражение теперь идеально). Если вещь действительна, вы просто возвращаете правильный кусок разбиения.
Я бы выразил формат как:
"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 +) $ Спасибо!
Единственная проблема с этим предложением - очень красивым и чистым - состоит в том, что группы не правы, потому что 1.2 из-за жадности захватит 1 в первой и 2 в третьей группе.
Имейте в виду, что регулярные выражения являются жадными, поэтому, если вы просто ищете в строке номера версии, а не в большом тексте, используйте ^ и $, чтобы отметить начало и конец вашей строки. Регулярное выражение от Грега, кажется, работает нормально (просто попробовал быстро в моем редакторе), но в зависимости от вашей библиотеки / языка первая часть может соответствовать «*» в неправильных номерах версий. Может, мне чего-то не хватает, потому что я не использовал 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 выглядит самым простым.
Вам также следует проверить неправильные. Вы пропустили процитировать точки OneByOne.
Обновлено с точками и другими регулярными выражениями
(?ms)^((?:\d+(?!\.\*)\.)+)(\d+)?(\.\*)?$|^(\d+)\.\*$|^(\*|\d+)$
Точно соответствует вашим 6 первым примерам и отвергает 4 других
Вы можете удалить '(? Ms)'
Я использовал его, чтобы указать, что это регулярное выражение будет применяться к многострочным через QuickRex
Не знаю, на какой платформе вы работаете, но в .NET есть класс System.Version, который будет анализировать номера версий «n.n.n.n» за вас.
Нет, там с версии 1.0
Это тоже соответствует 1.2.3. *
^(*|\d+(.\d+){0,2}(.*)?)$
Я бы предложил менее элегантный:
(* | \ d + (. \ d +)? (. *)?) | \ d +. \ d +. \ d +)
Спасибо за все ответы! Это туз :)
Основываясь на ответе OneByOne (который мне показался наиболее простым), я добавил несколько групп без захвата (части '(?:' - спасибо VonC за то, что познакомил меня с группами без захвата!), Поэтому группы, которые захватывают только содержат цифры или символ *.
^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$
Всем большое спасибо!
Не могли бы вы вместо этого добавить это как правку к вашему вопросу? Таким образом, правильные ответы будут ближе к началу
С именами групп: ^ (? :(? <major> \ d +) \.)? (? :(? <minor> \ d +) \.)? (? <build> * | \ d +) $
поддержка семверсии (немного больше). - "1.2.3-alpha + abcdedf.lalal" -match "^ (?: (\ D +) \.)? (? (?: (\ D +) \.)? (* | \ D +)? (?: \ - ([A-Za-z0-9 \.] +))? (?: \ + ([A-Za-z0-9 \.] +))? $ "
Помните, что в случае версии, состоящей из одного номера, ей будет соответствовать третий (\*|\d+), а не первая группа ^(?:(\d+)\.)?.
Еще одна попытка:
^(((\d+)\.)?(\d+)\.)?(\d+|\*)$
Это дает три части в группах 4,5,6 НО: Они выровнены по правому краю. Таким образом, первое ненулевое значение из 4,5 или 6 дает поле версии.
Кажется, довольно сложно иметь регулярное выражение, которое делает именно то, что вы хотите (т.е. принимайте только те случаи, которые вам нужны, и отклоняете все, другие и возвращают некоторые группы для трех компонентов). Я попробовал и придумал следующее:
^(\*|(\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]+)))?)))?))$

Я видел много ответов, но ... у меня есть новый. По крайней мере, у меня это работает. Я добавил новое ограничение. Номера версий (основные, второстепенные или патч) не могут начинаться с нуля, за которым следуют другие.
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.
@JochemSchulenklopper спасибо, я знаю о SemVer, хотя в вопросе ничего не упоминается о SemVer.
Истинный. Меня направил на этот вопрос коллега о синтаксическом анализе строк SemVer, так что я прочитал ответы на эти вопросы.
Еще одно решение:
^[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.]*$'
Это гарантирует, что версия,
Одним из недостатков является то, что версия может даже заканчиваться на '.' Но он может обрабатывать неограниченную длину версии (безумное управление версиями, если вы хотите это так называть)
Совпадения:
Если вы не недовольны "." окончание, может быть, вы можете объединить концы с логикой
Чтобы избавиться от последней цифры, возможно, вы захотите попробовать следующее: (\d+)(.\d+)*
Для разбора номеров версий, которые следуют этим правилам: - Только цифры и точки - Не может начинаться или заканчиваться точкой - Не может быть двух точек вместе
Это помогло мне.
^(\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|\*)+
Я не уверен, что «простой» возможен.