Una regex per l'analisi del numero di versione


86

Ho un numero di versione del seguente modulo:

version.release.modification

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

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 rilascio, versione e modifica?


Non sono sicuro che sia possibile uno "semplice".
svrist

Risposte:


98

Esprimerei il formato come:

"1-3 componenti separati da punti, ciascuno numerico tranne l'ultimo può essere *"

Come regexp, è:

^(\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 affrontare questo problema complicando la regexp o elaborando i gruppi abbinati.

Nella mia soluzione, i gruppi catturano il file "." personaggi. Questo può essere affrontato usando gruppi che non catturano come nella risposta di ajborley.

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

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

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

Che in realtà non è più breve della separazione "." ]


1
L'aggiunta di alcuni gruppi non di acquisizione (vedere la mia risposta di seguito) significa che i gruppi di acquisizione non acquisiscono il carattere finale "." ^ (?: (\ d +) \.)? (?: (\ d +) \.)? (* | \ d +) $ Grazie!
Andrew Borley,

L'unico problema con quella - essendo una proposta molto bella e pulita - è che i gruppi non hanno ragione perché 1.2 catturerà 1 nel primo e 2 nel terzo gruppo per avidità.
jrudolph

40

Usa regex e ora hai due problemi. Vorrei dividere la cosa su punti ("."), Quindi assicurarmi che ogni parte sia un carattere jolly o un insieme di cifre (l'espressione regolare è perfetta ora). Se la cosa è valida, restituisci semplicemente la parte corretta della divisione.


12

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 ". *" Opzionale. Questa regex accetterebbe 1.2.3. * Che può o non può essere consentito nella tua applicazione.

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


Bella risposta! Penso che dovresti scambiare * senza caratteri di escape con {0,2} per impedire la corrispondenza 1.2.3.4. A seconda della tua libreria regexp potresti voler racchiudere il pattern in ^ (<pattern>) $ se puoi solo fare una ricerca invece che una corrispondenza.
Dave Webb

Una leggera modifica a ^ (* | \ d + (\. \ D +) {0,1} (?: (\. *)? | (\. \ D +)?)) $ Invaliderebbe anche 1.2.3. *
Pieter

2
Pieter: Penso che per ora mi fermerò dove sono. Questo sta rapidamente entrando nel territorio "ora hai due problemi". :)
Greg Hewgill,

12

Grazie per tutte le risposte! Questo è l'asso :)

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

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

Grazie mille a tutti!


1
Potresti invece aggiungerlo come modifica alla tua domanda? In questo modo le risposte giuste sono vicine alla cima
svrist

1
Con i nomi dei gruppi: ^ (? :(? <major> \ d +) \.)? (?
:(

1
supporto semversion (un po 'di più). - "1.2.3-alpha + abcdedf.lalal" -match "^ (?: (\ D +) \.)? (?: (\ D +) \.)? (* | \ D +)? (?: \ - ([A-Za-z0-9 \.] +))? (?: \ + ([A-Za-z0-9 \.] +))? $ "
Sam

Attenzione che nel caso di una versione composta da un unico numero verrà abbinata dal terzo e (\*|\d+)non dal primo ^(?:(\d+)\.)?gruppo.
Piotr Dobrogost

9

I miei 2 centesimi: avevo questo scenario: dovevo analizzare i numeri di versione da una stringa letterale. (So ​​che questo è molto diverso dalla domanda originale, ma googling per trovare un'espressione regolare per l'analisi del numero di versione ha mostrato questo thread in alto, quindi aggiungi questa risposta qui)

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

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

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

Un piccolo snippet C # per testarlo è simile al 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        
}

So che stai descrivendo una situazione e un caso alternativi, ma solo per essere completo: SemVer 'richiede' che la stringa della versione sia del formato X.Y.Z(quindi, esattamente tre parti), dove X e Y devono essere numeri interi non negativi e no zeri iniziali aggiuntivi. Vedi semver.org .
Jochem Schulenklopper

1
@JochemSchulenklopper grazie, sono a conoscenza di SemVer, anche se la domanda non menziona nulla su SemVer.
Sudhanshu Mishra

1
Vero. Sono stato indirizzato a questa domanda da un collega sull'analisi delle stringhe SemVer, in modo che abbia incorniciato la mia lettura delle risposte.
Jochem Schulenklopper

8

Non so su quale piattaforma ti trovi, ma in .NET c'è la classe System.Version che analizzerà i numeri di versione "nnnn" per te.


No, è presente dalla versione 1.0
Duncan Smart

6

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

Sarebbe bello, dal momento che OneByOne sembra il più semplice.
jrudolph

Dovresti testare anche quelli sbagliati. Ti sei perso per citare i punti di OneByOne.
jrudolph

Aggiornato con i punti e più espressioni regolari
svrist

5
^(?:(\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 XYZ utilizzando * o {2} invece di {0,2}


5

Questo dovrebbe funzionare per quello che hai stabilito. Fa perno sulla posizione del carattere jolly ed è un'espressione regolare annidata:

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

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


5

Ho visto molte risposte, ma ... ne ho una nuova. Almeno per me funziona. Ho aggiunto una nuova restrizione. I numeri di versione non possono iniziare (maggiore, minore o patch) con zeri seguiti 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 su un precedente. Ma vedo meglio questa soluzione ... per me;)

Godere!!!


4

Un altro tentativo:

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

Questo dà le tre parti nei 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 fornisce 1,2,3
  • 1.2. * Dà 1,2, *
  • 1.2 dà null, 1,2
  • *** restituisce null, null, *
  • 1. * restituisce null, 1, *

4

Avevo la necessità di cercare / abbinare i numeri di versione, che segue la convenzione Maven o anche solo una cifra. Ma nessun qualificatore in ogni caso. Era strano, mi ci è voluto del tempo poi mi è venuto in mente questo:

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

Questo assicura che la versione,

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

Uno svantaggio è che la versione può persino terminare con "." Ma può gestire una durata indefinita della versione (versione folle se vuoi chiamarla così)

Partite:

  • 1.2.3
  • 1.09.5
  • 3.4.4.5.7.8.8.
  • 23.6.209.234.3

Se non sei insoddisfatto di "." finale, potresti combinarlo con fini con logica


Per sbarazzarsi dell'ultima cifra, forse ti piacerebbe provare questo:(\d+)(.\d+)*
cassioso

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

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

  • gruppo 1: maggiore o maggiore, minore o "*"
  • gruppo 2 se esiste: minore o *
  • gruppo 3 se esiste: *

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


3

Anche questo corrisponde a 1.2.3. *

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

Proporrei il meno elegante:

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


3

Tieni presente che le espressioni regolari sono avide, quindi se stai cercando solo 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 a "*" con numeri di versione sbagliati. Forse mi manca qualcosa, dato che non uso Regexp da circa un anno.

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

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

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


3

Sembra piuttosto difficile avere una regex che fa esattamente quello che vuoi (cioè accetta solo i casi che ti servono e rifiuta tutti gli altri e restituisci alcuni gruppi per i tre componenti). Ho provato e ho trovato questo:

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

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

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


3

Specificare gli 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>

3

La mia opinione su questo, come un buon esercizio - vparse , che ha una 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;
}

3

Per analizzare i numeri di versione che seguono queste regole: - Sono solo cifre e punti - Non possono iniziare o finire con un punto - Non possono essere due punti insieme

Questo mi ha fatto il trucco.

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

I casi validi sono:

1, 0.1, 1.2.1



2

A volte i numeri di versione potrebbero contenere informazioni minore alfanumerico (ad esempio 1.2.0b o 1.2.0-beta ). In questo caso sto usando questa regex:

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

1

Ho trovato questo e funziona per me:

/(\^|\~?)(\d|x|\*)+\.(\d|x|\*)+\.(\d|x|\*)+
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.