it-swarm-eu.dev

Ein regulärer Ausdruck für das Parsen von Versionsnummern

Ich habe eine Versionsnummer des folgenden Formulars:

version.release.modification

dabei sind Version, Freigabe und Änderung entweder ein Satz von Ziffern oder das Platzhalterzeichen '*'. Darüber hinaus fehlt möglicherweise eine dieser Nummern (und alle vorangegangenen).

Das Folgende ist also gültig und wird analysiert als:

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

Diese sind jedoch nicht gültig:

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

Kann mir jemand einen nicht allzu komplexen regulären Ausdruck zur Verfügung stellen, um die Versions-, Versions- und Änderungsnummern zu validieren und abzurufen?

73
Andrew Borley

Ich würde das Format ausdrücken als:

1-3 durch Punkte getrennte Komponenten, wobei jede numerisch ist, außer dass die letzte * sein kann

Als regulärer Ausdruck ist das:

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

[Bearbeiten zum Hinzufügen: Diese Lösung ist eine präzise Methode zur Validierung, es wurde jedoch darauf hingewiesen, dass das Extrahieren der Werte zusätzlichen Arbeitsaufwand erfordert. Es ist eine Frage des Geschmacks, ob dies durch Komplizieren des regulären Ausdrucks oder durch Verarbeiten der übereinstimmenden Gruppen behoben werden soll.

In meiner Lösung erfassen die Gruppen die "." Zeichen. Dies kann mit nicht erfassenden Gruppen wie in Ajborleys Antwort behandelt werden.

Die Gruppe ganz rechts erfasst auch dann die letzte Komponente, wenn weniger als drei Komponenten vorhanden sind. Bei einer Eingabe mit zwei Komponenten werden beispielsweise die erste und die letzte Gruppe erfasst und die mittlere Gruppe undefiniert. Ich denke, dies kann von nicht gierigen Gruppen erledigt werden, wenn sie unterstützt werden.

Perl-Code zur Behebung beider Probleme nach dem regulären Ausdruck könnte etwa so aussehen:

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

Was eigentlich nicht kürzer ist als das Aufteilen auf "."]

76
Steve Jessop

Benutze Regex und jetzt hast du zwei Probleme. Ich würde das Ding auf Punkte (".") Aufteilen und dann sicherstellen, dass jeder Teil entweder ein Platzhalter oder ein Satz von Ziffern ist (Regex ist jetzt perfekt). Wenn die Sache gültig ist, geben Sie einfach den korrekten Teil der Teilung zurück.

38
Paweł Hajdan

Das könnte funktionieren:

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

Auf der obersten Ebene ist "*" ein Sonderfall einer gültigen Versionsnummer. Ansonsten beginnt es mit einer Zahl. Dann gibt es null, eins oder zwei ".nn" Sequenzen, gefolgt von einem optionalen ". *". Dieser reguläre Ausdruck akzeptiert 1.2.3. *, Was in Ihrer Anwendung möglicherweise zulässig ist oder nicht.

Der Code zum Abrufen der übereinstimmenden Sequenzen, insbesondere der (\.\d+){0,2} Teil, hängt von Ihrer speziellen Regex-Bibliothek ab.

11
Greg Hewgill

Danke für all die Antworten! Das ist Ass :)

Basierend auf der Antwort von OneByOne (die mir am einfachsten erschien) habe ich einige nicht erfassende Gruppen hinzugefügt (die "?:" - Teile - dank VonC für die Einführung in nicht erfassende Gruppen!), Also die Gruppen, die nur erfassen Enthält die Ziffern oder das Zeichen *.

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

Vielen Dank an alle!

11
Andrew Borley

Meine 2 Cent: Ich hatte dieses Szenario: Ich musste Versionsnummern aus einem String-Literal analysieren. (Ich weiß, dass dies sehr anders ist als die ursprüngliche Frage, aber beim googeln nach einem regulären Ausdruck zum Parsen der Versionsnummer wurde dieser Thread oben angezeigt. Fügen Sie diese Antwort hier hinzu.)

Das Zeichenkettenliteral würde also ungefähr so ​​lauten: "Service Version 1.2.35.564 läuft!"

Ich musste den 1.2.35.564 aus diesem Literal herauslesen. Ausgehend von @ajborley lautet mein regulärer Ausdruck wie folgt:

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

Ein kleines C # -Snippet zum Testen sieht wie folgt aus:

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

Sie wissen nicht, auf welcher Plattform Sie sich befinden, aber in .NET gibt es die System.Version-Klasse, die "n.n.n.n" -Versionsnummern für Sie parst.

7
Duncan Smart

Ich stimme eher einem geteilten Vorschlag zu.

Ich habe einen "Tester" für Ihr Problem in Perl erstellt

#!/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";
}

Aktueller Output:

> 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

Dies sollte für das funktionieren, was Sie festgelegt haben. Es hängt von der Platzhalterposition ab und ist ein verschachtelter regulärer Ausdruck:

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

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

4
nomuus

Ich habe viele Antworten gesehen, aber ... ich habe eine neue. Das funktioniert zumindest bei mir. Ich habe eine neue Einschränkung hinzugefügt. Versionsnummern können nicht mit Nullen beginnen (Dur, Moll oder Patch), gefolgt von anderen.

01.0.0 ist nicht gültig 1.0.0 ist gültig 10.0.10 ist gültig 1.0.0000 ist ungültig

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

Es basiert auf einem früheren. Aber ich sehe diese Lösung besser ... für mich;)

Genießen!!!

4
Israel Romero

Noch ein Versuch:

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

Dies ergibt die drei Teile in Gruppen 4,5,6, ABER: Sie sind nach rechts ausgerichtet. Das erste von 4,5 oder 6, das nicht null ist, gibt das Versionsfeld an.

  • 1.2.3 ergibt 1,2,3
  • 1.2. * Ergibt 1,2, *
  • 1,2 ergibt null, 1,2
  • *** gibt null, null, *
  • 1. * gibt null, 1, *
3
jrudolph
^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$

Vielleicht könnte eine prägnantere sein:

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

Dies kann dann auf 1.2.3.4.5. * Erweitert oder mit * oder {2} anstelle von {0,2} genau auf X.Y.Z beschränkt werden.

3
ofaurax

Ich musste nach Versionsnummern suchen/suchen, die der Maven-Konvention oder sogar nur einer Ziffer entsprechen. Aber kein Qualifier auf jeden Fall. Es war merkwürdig, es hat mich Zeit gekostet, dann kam ich auf folgendes:

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

Dies stellt sicher, dass die Version,

  1. Beginnt mit einer Ziffer
  2. Kann eine beliebige Anzahl von Ziffern haben
  3. Nur Ziffern und '.' sind erlaubt

Ein Nachteil ist, dass die Version sogar mit '.' Enden kann. Aber es kann eine unbestimmte Länge der Version verarbeiten (verrückte Versionierung, wenn Sie es so nennen wollen)

Streichhölzer:

  • 1.2.3
  • 1.09.5
  • 3.4.4.5.7.8.8.
  • 23.6.209.234.3

Wenn Sie mit '.' Nicht unzufrieden sind ending, kann man mit endswith logic kombinieren

3
Shiva

Es scheint ziemlich schwer zu sein, einen regulären Ausdruck zu haben, der genau das tut, was Sie wollen (dh nur die Fälle akzeptieren, die Sie benötigen, und ablehnen alle andere und einige Gruppen für die drei zurückgeben Komponenten). Ich habe es ausprobiert und mir Folgendes ausgedacht:

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

IMO (ich habe es nicht ausführlich getestet) sollte dies als Validator für die Eingabe gut funktionieren, aber das Problem ist, dass diese Regex keine Möglichkeit zum Abrufen der Komponenten bietet. Dafür müssen Sie noch eine Teilperiode durchführen.

Diese Lösung ist kein All-in-One-System, muss jedoch in den meisten Fällen nicht programmiert werden. Dies hängt natürlich von anderen Einschränkungen ab, die Sie möglicherweise in Ihrem Code haben.

2
rslite

Beachten Sie, dass reguläre Ausdrücke gierig sind. Wenn Sie also nur in der Versionsnummer und nicht in einem größeren Text suchen, markieren Sie mit ^ und $ den Anfang und das Ende Ihrer Zeichenfolge. Der reguläre Ausdruck von Greg scheint gut zu funktionieren (habe es nur in meinem Editor kurz versucht), aber abhängig von Ihrer Bibliothek/Sprache kann der erste Teil immer noch mit dem "*" in den falschen Versionsnummern übereinstimmen. Vielleicht fehlt mir etwas, da ich Regexp seit ungefähr einem Jahr nicht mehr verwendet habe.

Dies sollte sicherstellen, dass Sie nur die richtigen Versionsnummern finden können:

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

edit: eigentlich hat greg sie schon hinzugefügt und sogar seine lösung verbessert, ich bin zu langsam :)

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

Entspricht genau Ihren 6 ersten Beispielen und lehnt die 4 anderen ab

  • gruppe 1: major oder major.minor oder '*'
  • gruppe 2 falls vorhanden: minderjährig oder *
  • gruppe 3 falls vorhanden: *

Sie können '(? Ms)' entfernen
Ich habe damit angegeben, dass dieser reguläre Ausdruck auf mehrere Zeilen angewendet werden soll, und zwar durch QuickRex

2
VonC

XSD-Elemente angeben:

<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

Dies entspricht auch 1.2.3. *

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

Ich würde das weniger elegante vorschlagen:

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

2
Victor

Ich nehme dies als eine gute Übung - vparse , die eine winzige Quelle hat, mit einer einfachen Funktion:

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

Eine weitere Lösung:

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