web-dev-qa-db-fra.com

Une expression régulière pour l'analyse du numéro de version

J'ai un numéro de version du formulaire suivant:

version.release.modification

où version, version et modification sont soit un ensemble de chiffres, soit le caractère générique "*". En outre, l'un de ces numéros (et tout précédent.) Peut être manquant.

Les éléments suivants sont donc valides et analysés comme:

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

Mais ceux-ci ne sont pas valables:

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

Quelqu'un peut-il me fournir une expression rationnelle pas trop complexe pour valider et récupérer les numéros de version, de version et de modification?

73
Andrew Borley

J'exprimerais le format comme:

"1-3 composants séparés par des points, chacun étant numérique sauf que le dernier peut être *"

En tant que regexp, c'est:

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

[Modifier pour ajouter: cette solution est un moyen concis de valider, mais il a été souligné que l'extraction des valeurs nécessite un travail supplémentaire. C'est une question de goût de traiter cela en compliquant l'expression régulière ou en traitant les groupes correspondants.

Dans ma solution, les groupes capturent le "." personnages. Cela peut être traité en utilisant des groupes non capturants comme dans la réponse d'Ajborley.

En outre, le groupe le plus à droite capturera le dernier composant, même s'il y a moins de trois composants, et ainsi, par exemple, une entrée à deux composants entraîne la capture des premier et dernier groupes et le milieu non défini. Je pense que cela peut être traité par des groupes non gourmands lorsque cela est soutenu.

Le code Perl pour traiter les deux problèmes après l'expression rationnelle pourrait ressembler à ceci:

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

Ce qui n'est pas vraiment plus court que le fractionnement sur "."]

76
Steve Jessop

tilisez regex et maintenant vous avez deux problèmes. Je diviserais la chose sur des points ("."), Puis m'assurer que chaque partie est soit un caractère générique ou un ensemble de chiffres (regex est parfait maintenant). Si la chose est valide, vous retournez simplement le morceau correct de la division.

38
Paweł Hajdan

Cela pourrait fonctionner:

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

Au niveau supérieur, "*" est un cas particulier d'un numéro de version valide. Sinon, cela commence par un nombre. Il y a ensuite zéro, une ou deux séquences ".nn", suivies d'un ". *" Facultatif. Ce regex accepterait 1.2.3. * Qui peut ou non être autorisé dans votre application.

Le code pour récupérer les séquences correspondantes, en particulier le (\.\d+){0,2} partie, dépendra de votre bibliothèque regex particulière.

11
Greg Hewgill

Merci pour toutes les réponses! C'est as :)

Sur la base de la réponse de OneByOne (qui m'a paru la plus simple), j'ai ajouté des groupes non capturants (les parties '(?:' - merci à VonC de m'avoir présenté des groupes non capturants!), Donc les groupes qui capturent uniquement contiennent les chiffres ou le caractère *.

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

Un grand merci à tous!

11
Andrew Borley

Mes 2 cents: J'ai eu ce scénario: j'ai dû analyser les numéros de version d'un littéral de chaîne. (Je sais que c'est très différent de la question d'origine, mais googler pour trouver un regex pour analyser le numéro de version a montré ce fil en haut, donc ajoutez cette réponse ici)

Ainsi, le littéral de chaîne serait quelque chose comme: "La version de service 1.2.35.564 est en cours d'exécution!"

J'ai dû analyser le 1.2.35.564 de ce littéral. S'inspirant de @ajborley, mon expression régulière est la suivante:

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

Un petit extrait C # pour tester cela ressemble à ci-dessous:

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

Je ne sais pas sur quelle plateforme vous vous trouvez, mais dans .NET, il y a la classe System.Version qui analysera les numéros de version "n.n.n.n" pour vous.

7
Duncan Smart

J'ai tendance à être d'accord avec la suggestion partagée.

Ive a créé un "testeur" pour votre problème en 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";
}

Sortie courant:

> 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

Cela devrait fonctionner pour ce que vous avez stipulé. Il dépend de la position du joker et est une expression rationnelle imbriquée:

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

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

4
nomuus

J'ai vu beaucoup de réponses, mais ... j'en ai une nouvelle. Cela fonctionne au moins pour moi. J'ai ajouté une nouvelle restriction. Les numéros de version ne peuvent pas commencer (majeur, mineur ou correctif) avec des zéros suivis par d'autres.

01.0.0 n'est pas valide 1.0.0 est valide 10.0.10 est valide 1.0.0000 n'est pas valide

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

Il est basé sur un précédent. Mais je vois mieux cette solution ... pour moi;)

Prendre plaisir!!!

4
Israel Romero

Un autre essai:

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

Cela donne les trois parties dans les groupes 4,5,6 MAIS: elles sont alignées à droite. Ainsi, le premier non nul de 4,5 ou 6 donne le champ de version.

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

Une solution plus concise pourrait être:

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

Cela peut ensuite être amélioré à 1.2.3.4.5. * Ou limité exactement à X.Y.Z en utilisant * ou {2} au lieu de {0,2}

3
ofaurax

J'avais besoin de rechercher/faire correspondre des numéros de version, qui suivent la convention maven ou même un seul chiffre. Mais aucun qualificatif en tout cas. C'était particulier, cela m'a pris du temps, puis j'ai trouvé ceci:

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

Cela garantit que la version,

  1. Commence par un chiffre
  2. Peut avoir n'importe quel nombre de chiffres
  3. Seuls les chiffres et "." sont autorisés

Un inconvénient est que la version peut même se terminer par "." Mais il peut gérer une longueur de version indéfinie (versioning fou si vous voulez l'appeler ainsi)

Allumettes:

  • 1.2.3
  • 1.09.5
  • 3.4.4.5.7.8.8.
  • 23.6.209.234.3

Si vous n'êtes pas mécontent de "." fin, peut-être que vous pouvez combiner avec une logique de fin

3
Shiva

Il semble assez difficile d'avoir une expression régulière qui fait exactement ce que vous voulez (c'est-à-dire n'accepter que les cas dont vous avez besoin et rejeter tous autres et retourne quelques groupes pour les trois composants). Je l'ai essayé et trouvé ceci:

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

IMO (je n'ai pas testé longuement) cela devrait fonctionner correctement en tant que validateur pour l'entrée, mais le problème est que cette expression régulière n'offre pas un moyen de récupérer les composants. Pour cela, vous devez toujours faire une scission sur la période.

Cette solution n'est pas tout-en-un, mais la plupart du temps en programmation, elle n'en a pas besoin. Bien sûr, cela dépend d'autres restrictions que vous pourriez avoir dans votre code.

2
rslite

Gardez à l'esprit que les expressions rationnelles sont gourmandes, donc si vous recherchez simplement dans la chaîne de numéro de version et non dans un texte plus gros, utilisez ^ et $ pour marquer le début et la fin de votre chaîne. L'expression régulière de Greg semble fonctionner correctement (je l'ai juste essayé rapidement dans mon éditeur), mais selon votre bibliothèque/langue, la première partie peut toujours correspondre au "*" dans les mauvais numéros de version. Peut-être que je manque quelque chose, car je n'ai pas utilisé Regexp depuis un an environ.

Cela devrait vous assurer que vous ne pouvez trouver que les numéros de version corrects:

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

edit: effectivement greg les a déjà ajoutés et même amélioré sa solution, je suis trop lent :)

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

Correspond exactement à vos 6 premiers exemples et rejette les 4 autres

  • groupe 1: majeur ou majeur.mineur ou '*'
  • groupe 2 s'il existe: mineur ou *
  • groupe 3 s'il existe: *

Vous pouvez supprimer '(? Ms)'
Je l'ai utilisé pour indiquer à ce regexp d'être appliqué sur plusieurs lignes via QuickRex

2
VonC

Spécification des éléments 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

Cela correspond aussi à 1.2.3. *

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

Je proposerais le moins élégant:

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

2
Victor

Mon point de vue sur cela, comme un bon exercice - vparse , qui a un petite source , avec une fonction simple:

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

Encore une solution:

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