it-swarm-pt.tech

Uma regex para análise do número da versão

Eu tenho um número de versão do seguinte formulário:

version.release.modification

onde versão, liberação e modificação são um conjunto de dígitos ou o caractere curinga '*'. Além disso, qualquer um desses números (e qualquer precedente) pode estar ausente.

Portanto, o seguinte é válido e analise como:

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

Mas estes não são válidos:

*.12
*123.1
12*
12.*.34

Alguém pode me fornecer uma regex não muito complexa para validar e recuperar os números de versão, versão e modificação?

73
Andrew Borley

Eu expressaria o formato como:

"1-3 componentes separados por pontos, cada numérico, exceto que o último pode ser *"

Como regexp, é isso:

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

[Editar para adicionar: esta solução é uma maneira concisa de validar, mas foi apontado que a extração dos valores requer trabalho extra. É uma questão de gosto lidar com isso complicando a regexp ou processando os grupos correspondentes.

Na minha solução, os grupos capturam os caracteres ".". Isso pode ser resolvido com o uso de grupos não capturadores, como na resposta de ajborley.

Além disso, o grupo mais à direita capturará o último componente, mesmo que haja menos de três componentes; portanto, por exemplo, uma entrada de dois componentes resulta na captura do primeiro e do último grupo e do meio indefinido. Eu acho que isso pode ser tratado por grupos não gananciosos quando apoiados.

O código Perl para lidar com os dois problemas após o regexp pode ser algo como isto:

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

O que não é realmente mais curto que dividir em "."]

76
Steve Jessop

se regex e agora você tem dois problemas. Eu dividiria a coisa em pontos (".") E, em seguida, certifique-se de que cada parte seja um curinga ou um conjunto de dígitos (regex é perfeito agora). Se a coisa for válida, basta retornar a parte correta da divisão.

38
Paweł Hajdan

Isso pode funcionar:

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

No nível superior, "*" é um caso especial de um número de versão válido. Caso contrário, começa com um número. Depois, há zero, uma ou duas seqüências ".nn", seguidas por um opcional ". *". Este regex aceita 1.2.3. *, Que pode ou não ser permitido em sua inscrição.

O código para recuperar as seqüências correspondentes, especialmente a parte (\.\d+){0,2}, dependerá da sua biblioteca regex específica.

11
Greg Hewgill

Obrigado por todas as respostas! Isso é ace :)

Com base na resposta do OneByOne (que me pareceu mais simples), adicionei alguns grupos não capturadores (as partes '(?:' - obrigado ao VonC por me apresentar a grupos não capturadores!), Portanto, os grupos que capturam apenas contém os dígitos ou o caractere *.

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

Muito obrigado a todos!

11
Andrew Borley

Meus 2 centavos: Eu tive esse cenário: tive que analisar os números de versão de um literal de seqüência de caracteres. (Eu sei que isso é muito diferente da pergunta original, mas pesquisar no Google por encontrar uma regex para analisar o número da versão mostrou esse segmento na parte superior, adicionando esta resposta aqui)

Portanto, a string literal seria algo como: "A versão 1.2.35.564 do serviço está sendo executada!"

Eu tive que analisar o 1.2.35.564 deste literal. Tomando uma sugestão de @ajborley, meu regex é o seguinte:

(?:(\d+)\.)?(?:(\d+)\.)?(?:(\d+)\.\d+)

Um pequeno trecho de C # para testar isso se parece abaixo:

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        
}
7
Sudhanshu Mishra

Não sei em qual plataforma você está, mas no .NET há a classe System.Version que analisará os números de versão "n.n.n.n" para você.

7
Duncan Smart

Costumo concordar com sugestões divididas.

Eu criei um "testador" para o seu problema no 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";
}

Saída atual:

> 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

Isso deve funcionar para o que você estipulou. Depende da posição do curinga e é um regex aninhado:

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

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

4
nomuus

Eu já vi muitas respostas, mas ... eu tenho uma nova. Funciona para mim pelo menos. Eu adicionei uma nova restrição. Os números de versão não podem iniciar (maior, menor ou patch) com zeros seguidos por outros.

01.0.0 não é válido 1.0.0 é válido 10.0.10 é válido 1.0.0000 não é válido

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

É baseado em um anterior. Mas eu vejo essa solução melhor ... para mim;)

Desfrutar!!!

4
Israel Romero

Outra tentativa:

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

Isso fornece as três partes nos grupos 4,5,6 MAS: Elas estão alinhadas à direita. Portanto, o primeiro não nulo de 4,5 ou 6 fornece o campo da versão.

  • 1.2.3 fornece 1,2,3
  • 1.2. * Fornece 1,2, *
  • 1,2 dá nulo, 1,2
  • *** fornece nulo, nulo, *
  • 1. * fornece nulo, 1, *
3
jrudolph
^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$

Talvez um mais conciso possa ser:

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

Isso pode ser aprimorado para 1.2.3.4.5. * Ou restrito exatamente a X.Y.Z usando * ou {2} em vez de {0,2}

3
ofaurax

Eu tinha um requisito para procurar/combinar números de versão, que seguem a convenção do maven ou mesmo apenas um dígito. Mas nenhum qualificador, em qualquer caso. Era peculiar, demorei um pouco e cheguei a isso:

'^[0-9][0-9.]*$'

Isso garante que a versão,

  1. Começa com um dígito
  2. Pode ter qualquer número de dígitos
  3. Apenas dígitos e '.' são autorizadas

Uma desvantagem é que a versão pode terminar com '.' Mas ele pode lidar com um comprimento indefinido da versão (versão maluca, se você quiser chamar assim)

Fósforos:

  • 1.2.3
  • 1.09.5
  • 3.4.4.5.7.8.8.
  • 23.6.209.234.3

Se você não está descontente com '.' final, pode ser que você possa combinar com final com lógica

3
Shiva

Parece muito difícil ter um regex que faça exatamente o que você deseja (por exemplo, aceite apenas os casos necessários e rejeite todos outros e retorne alguns grupos para os três componentes). Eu tentei e criei isso:

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

IMO (eu não testei extensivamente) isso deve funcionar bem como um validador para a entrada, mas o problema é que esse regex não oferece uma maneira de recuperar os componentes. Para isso, você ainda precisa dividir o período.

Esta solução não é tudo em um, mas na maioria das vezes na programação não precisa. Obviamente, isso depende de outras restrições que você possa ter no seu código.

2
rslite

Lembre-se de que regexp é ganancioso; portanto, se você estiver apenas pesquisando na sequência do número da versão e não em um texto maior, use ^ e $ para marcar o início e o final da sequência. O regexp de Greg parece funcionar bem (tentei rapidamente no meu editor), mas, dependendo da sua biblioteca/idioma, a primeira parte ainda pode corresponder ao "*" nos números de versão incorretos. Talvez esteja faltando alguma coisa, pois não uso o Regexp há um ano ou mais.

Isso deve garantir que você encontre apenas os números de versão corretos:

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

edit: na verdade, o greg já os adicionou e até melhorou sua solução, sou muito lento :)

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

Corresponde exatamente aos seus 6 primeiros exemplos e rejeita os outros 4

  • grupo 1: maior ou maior. menor ou '*'
  • grupo 2 se existir: menor ou *
  • grupo 3 se existir: *

Você pode remover '(? Ms)'
Usei-o para indicar que esse regexp seria aplicado em várias linhas por meio de QuickRex

2
VonC

Especificando elementos 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

Também corresponde a 1.2.3. *

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

Eu proporia o menos elegante:

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

2
Victor

Minha opinião sobre isso, como um bom exercício - vparse , que possui um fonte minúscula , com uma função simples:

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

Mais uma solução:

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