Регулярное выражение для разбора номера версии
У меня есть номер версии следующей формы:
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