it-swarm.it

Una regex per l'analisi del numero di versione

Ho un numero di versione del seguente modulo:

version.release.modification

dove versione, versione e modifica sono un insieme di cifre o il carattere jolly '*'. Inoltre, potrebbe mancare uno di questi numeri (e qualsiasi precedente).

Quindi i seguenti sono validi e analizzati come:

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

Ma questi non sono validi:

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

Qualcuno può fornirmi una regex non troppo complessa per convalidare e recuperare i numeri di versione, versione e modifica?

73
Andrew Borley

Esprimerei il formato come:

"1-3 componenti separati da punti, ciascuno numerico ad eccezione del fatto che l'ultimo può essere *"

Come regexp, questo è:

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

[Modifica da aggiungere: questa soluzione è un modo conciso per convalidare, ma è stato sottolineato che l'estrazione dei valori richiede un lavoro extra. È una questione di gusti se affrontarlo complicando la regexp o elaborando i gruppi corrispondenti.

Nella mia soluzione, i gruppi acquisiscono il "." personaggi. Questo può essere affrontato usando gruppi non catturanti come nella risposta di Ajborley.

Inoltre, il gruppo più a destra acquisirà l'ultimo componente, anche se ci sono meno di tre componenti, quindi, ad esempio, un input a due componenti determina la cattura del primo e dell'ultimo gruppo e quello centrale non definito. Penso che questo possa essere affrontato da gruppi non avidi ove supportato.

Il codice Perl per affrontare entrambi i problemi dopo il regexp potrebbe essere qualcosa del genere:

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

Il che non è affatto più breve della divisione su "."]

76
Steve Jessop

sa regex e ora hai due problemi. Dividerei la cosa sui punti ("."), Quindi assicurerei che ogni parte sia un jolly o un insieme di cifre (regex è perfetto ora). Se la cosa è valida, devi solo restituire il blocco corretto della divisione.

38
Paweł Hajdan

Questo potrebbe funzionare:

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

Al livello superiore, "*" è un caso speciale di un numero di versione valido. Altrimenti, inizia con un numero. Quindi ci sono zero, una o due sequenze ".nn", seguite da un ". *" Facoltativo. Questa regex accetterebbe 1.2.3. * Che può o non può essere permesso nella tua domanda.

Il codice per recuperare le sequenze corrispondenti, in particolare (\.\d+){0,2} parte, dipenderà dalla tua particolare libreria regex.

11
Greg Hewgill

Grazie per tutte le risposte! Questo è asso :)

Sulla base della risposta di OneByOne (che mi è sembrata la più semplice), ho aggiunto alcuni gruppi non catturanti (le parti '(?:' - grazie a VonC per avermi introdotto ai gruppi non catturanti!), Quindi i gruppi che catturano solo contiene le cifre o il carattere *.

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

Mille grazie a tutti!

11
Andrew Borley

I miei 2 centesimi: ho avuto questo scenario: ho dovuto analizzare i numeri di versione da una stringa letterale. (So ​​che questo è molto diverso dalla domanda originale, ma cercare su Google una regex per l'analisi del numero di versione ha mostrato questo thread in alto, quindi aggiungendo questa risposta qui)

Quindi la stringa letterale sarebbe qualcosa del tipo: "La versione di servizio 1.2.35.564 è in esecuzione!"

Ho dovuto analizzare 1.2.35.564 da questo letterale. Prendendo spunto da @ajborley, il mio regex è il seguente:

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

Un piccolo frammento di C # per testarlo è il seguente:

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

Non so quale piattaforma stai utilizzando, ma in .NET c'è la classe System.Version che analizzerà i numeri di versione "n.n.n.n" per te.

7
Duncan Smart

Tendo ad essere d'accordo con il suggerimento diviso.

Ho creato un "tester" per il tuo problema in 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";
}

Uscita corrente:

> 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

Questo dovrebbe funzionare per quello che hai stipulato. Si basa sulla posizione del jolly ed è una regex nidificata:

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

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

4
nomuus

Ho visto molte risposte, ma ... ne ho una nuova. Funziona almeno per me. Ho aggiunto una nuova restrizione. I numeri di versione non possono iniziare (maggiori, minori o patch) con nessuno zero seguito da altri.

01.0.0 non è valido 1.0.0 è valido 10.0.10 è valido 1.0.0000 non è valido

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

È basato in uno precedente. Ma vedo questa soluzione meglio ... per me;)

Godere!!!

4
Israel Romero

Un altro tentativo:

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

Questo dà le tre parti in gruppi 4,5,6 MA: sono allineate a destra. Quindi il primo non nullo di 4,5 o 6 fornisce il campo versione.

  • 1.2.3 dà 1,2,3
  • 1.2. * Dà 1,2, *
  • 1.2 restituisce null, 1,2
  • *** dà null, null, *
  • 1. * dà null, 1, *
3
jrudolph
^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$

Forse uno più conciso potrebbe essere:

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

Questo può quindi essere migliorato a 1.2.3.4.5. * O limitato esattamente a X.Y.Z usando * o {2} invece di {0,2}

3
ofaurax

Avevo l'obbligo di cercare/abbinare i numeri di versione, che segue la convenzione maven o anche solo una singola cifra. Ma nessun qualificatore in ogni caso. Era strano, mi ci è voluto del tempo, poi ho pensato a questo:

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

Questo assicura che la versione,

  1. Inizia con una cifra
  2. Può avere un numero qualsiasi di cifre
  3. Solo cifre e "." sono ammessi

Uno svantaggio è che la versione può anche finire con '.' Ma può gestire una versione indefinita della versione (versione folle se si desidera chiamarla così)

Gli incontri:

  • 1.2.3
  • 1.09.5
  • 3.4.4.5.7.8.8.
  • 23.6.209.234.3

Se non sei insoddisfatto di "." alla fine, può darsi che tu possa combinare con estremità con la logica

3
Shiva

Sembra abbastanza difficile avere una regex che fa esattamente quello che vuoi (cioè accetta solo i casi di cui hai bisogno e rifiuta tutti altri e restituisce alcuni gruppi per i tre componenti). Ci ho provato e ho escogitato questo:

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

IMO (non ho ampiamente testato) questo dovrebbe funzionare bene come validatore per l'input, ma il problema è che questo regex non offre un modo per recuperare i componenti. Per questo devi ancora fare una divisione per periodo.

Questa soluzione non è all-in-one, ma la maggior parte delle volte in programmazione non è necessaria. Naturalmente questo dipende da altre restrizioni che potresti avere nel tuo codice.

2
rslite

Ricorda che regexp è avido, quindi se stai solo cercando all'interno della stringa del numero di versione e non all'interno di un testo più grande, usa ^ e $ per contrassegnare l'inizio e la fine della stringa. La regexp di Greg sembra funzionare bene (ho appena provato nel mio editor), ma a seconda della tua libreria/lingua la prima parte può ancora corrispondere al "*" all'interno dei numeri di versione sbagliati. Forse mi manca qualcosa, dato che non uso Regexp da circa un anno.

Questo dovrebbe assicurarsi di poter trovare solo i numeri di versione corretti:

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

modifica: in realtà greg li ha già aggiunti e persino migliorato la sua soluzione, sono troppo lento :)

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

Corrisponde esattamente ai tuoi primi 6 esempi e rifiuta gli altri 4

  • gruppo 1: major o major.minor o '*'
  • gruppo 2 se esiste: minore o *
  • gruppo 3 se esiste: *

Puoi rimuovere "(? Ms)"
L'ho usato per indicare a questa regexp da applicare su più righe attraverso QuickRex

2
VonC

Specifica degli elementi 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

Questo corrisponde anche a 1.2.3. *

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

Vorrei proporre il meno elegante:

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

2
Victor

La mia opinione su questo, come un buon esercizio - vparse , che ha un piccola fonte , con una semplice funzione:

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

Un'altra soluzione:

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