2008-09-17 9 views
61

I tienen un número de versión de la siguiente forma:una expresión regular para analizar el número de versión

version.release.modification

donde versión, la liberación y la modificación son o bien un conjunto de dígitos o el '*' comodín personaje. Además, puede faltar cualquiera de estos números (y cualquier otro anterior).

Así que lo siguiente es válido y analizar sintácticamente 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 

Pero estos no son válidos:

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

¿Alguien puede proporcionar una expresión regular no muy compleja para validar y recuperar la liberación , versión y números de modificación?

+0

Im no seguro de que es posible uno "simple". – svrist

Respuesta

51

me gustaría expresar el formato como:

"1-3 componentes separados por puntos, cada uno numérico, excepto que el último puede ser *"

Como una expresión regular, que es:

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

[Editar para añadir: esta solución es una forma concisa para validar, pero se ha señalado que la extracción de los valores requiere un trabajo extra. Es una cuestión de gusto tratar esto complicando la expresión regular, o procesando los grupos coincidentes.

En mi solución, los grupos de captura de las "." caracteres. Esto se puede tratar utilizando grupos no capturadores como en la respuesta de ajborley.

Además, el grupo de la derecha capturará el último componente, incluso si hay menos de tres componentes, por lo que una entrada de dos componentes da como resultado la captura del primer y último grupo y el del medio indefinido. Creo que esto puede ser tratado por grupos no codiciosos donde sea compatible.

código

Perl para hacer frente a ambos problemas después de la expresión regular podría ser algo como esto:

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

que no es realmente más corto que cualquier división en "." ]

+0

Agregar algunos grupos que no capturan (ver mi respuesta a continuación) significa que los grupos de captura no capturan el '.' ^ (?: (\ D +) \.)? (?: (\ D +) \.)? (\ * | \ D +) $ ¡Gracias! –

+0

El único problema con eso, al ser una propuesta muy agradable y limpia, es que los grupos no están bien porque 1.2 capturará 1 en el primero y 2 en el tercer grupo debido a la avaricia. – jrudolph

10

Esto podría funcionar:

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

En el nivel superior, "*" es un caso especial de un número de versión válida. De lo contrario, comienza con un número. Luego hay cero, uno o dos secuencias ".nn", seguidas de un ". *" Opcional. Esta expresión regular aceptaría 1.2.3. * Que puede o no estar permitido en su aplicación.

El código para la recuperación de las secuencias coincidentes, especialmente la parte (\.\d+){0,2}, dependerá de su biblioteca de expresiones regulares particular.

+0

¡Gran respuesta! Creo que deberías cambiar el * no guardado * por {0,2} para evitar la coincidencia 1.2.3.4. Dependiendo de su biblioteca de expresiones regulares, puede encerrar el patrón en^() $ si solo puede hacer una búsqueda en lugar de una coincidencia. –

+0

Buenos puntos, he mejorado la respuesta. –

+0

Una ligera alteración a^(\ * | \ d + (\. \ D +) {0,1} (?: (\. \ *)? | (\. \ D +)?)) $ Invalidaría 1.2.3.* también – Pieter

36

Usa regex y ahora tienes dos problemas. sin dividir la cosa en puntos (""), a continuación, asegúrese de que cada parte es o bien un comodín o un conjunto de dígitos (expresiones regulares es perfecto ahora). Si la cosa es válida, solo devuelve el fragmento correcto de la división.

1

Tenga en cuenta expresiones regulares son codiciosos , por lo tanto, si solo está buscando dentro de la cadena del número de versión y no dentro de un texto más grande, use^y $ para marcar el inicio y el final de la cadena. La expresión regular de Greg parece funcionar bien (solo le di una oportunidad rápida en mi editor), pero dependiendo de su biblioteca/idioma, la primera parte aún puede coincidir con el "*" dentro de los números de versión incorrectos. Tal vez me esté perdiendo algo, ya que no he usado Regexp durante un año más o menos.

Esto debe asegurarse de que sólo se puede encontrar los números de versión correcta:

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

edición: de hecho Greg los agregó ya e incluso mejoró su solución, soy demasiado lento :)

+0

ouch sí, no lo noté - gracias :) – FrankS

4

Tiendo a estar de acuerdo con la sugerencia de división.

he creado un "probador" para su problema 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"; 
} 

Salida de corriente:

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

Eso estaría bien, ya que OneByOne parece el más directo. – jrudolph

+0

También debe probar los incorrectos. Te perdiste citar los puntos de OneByOne. – jrudolph

+0

Actualizado con los puntos, y más expresiones regulares – svrist

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

coincide exactamente con sus 6 primeros ejemplos, y rechaza los otros 4

  • grupo 1: mayor o mayor.minor o ' *'
  • grupo 2 si existe: menor o *
  • grupo 3 si existe: *

Se puede quitar '(? M)'
lo utilicé para indicar a esta expresión regular para ser aplicado en multilíneas a través de QuickRex

5

No sé en qué plataforma está, pero en .NET existe la clase System.Version que analizará los números de versión "nnnn" por usted.

+1

Solo en .Net Framework 4 ...: \ –

+0

No, ha estado allí desde la versión 1.0 –

1

Esto coincide 1.2.3 * demasiado

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

yo propondría la menos elegante:

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

9

Gracias por todas las respuestas..! Esto es ace :)

Basado en la respuesta de OneByOne (que me pareció el más simple), agregué algunos grupos que no capturan (las partes '(?:') ¡Gracias a VonC por presentarme a grupos que no capturan!), por lo que los grupos que capturan solo contienen los dígitos o el carácter *.

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

Muchas gracias a todos!

+1

¿Podría agregar esto como una edición a su pregunta? De esta manera las respuestas correctas se encuentra cerca de la parte superior – svrist

+1

Con nombres de grupo: ^ \) (:( \ d +) \) ( \ * | (:( \ d +??).???.?? \ d +) $ – javacavaj

+1

soporte de semversion (un poco más). - "1.2.3-alpha + abcdedf.lalal" -match "^ (?: (\ D +) \.)? (?: (\ D +) \.)? (\ * | \ D +)? (?: \ - ([A-Za-z0-9 \.] +))? (?: \ + ([A-Za-z0-9 \.] +))? $ " – Sam

2

Otro intento:

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

Esto le da a las tres partes en grupos 4,5,6 PERO: Ellos están alineados a la derecha. Así que el primer no nulo de 4,5 o 6 da el campo de versión.

  • 1.2.3 da 1,2,3
  • 1.2. * Da 1,2, *
  • 1,2 da null, 1,2
  • *** da null, null, *
  • 1. * da nula, 1, *
1

parece bastante difícil tener una expresión regular que hace exactamente lo que quiere (es decir, aceptar sólo los casos que usted necesita y rechazar todas las demás y devuelven algunos grupos para los tres componentes). Tengo que darle una oportunidad y llegar a esto:

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

OMI (no he probado extensivamente) esto debería funcionar bien como un validador para la entrada, pero el problema es que esta expresión regular no lo hace ofrecer una forma de recuperar los componentes. Para eso, todavía tienes que hacer un split en el período.

Esta solución no es todo en uno, pero la mayoría de las veces en la programación no es necesario. Por supuesto, esto depende de otras restricciones que pueda tener en su código.

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

Tal vez una más concisa podrían ser:

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

Esto puede ser mejorada a 1.2.3.4.5 * o restringido exactamente a XYZ usando * o {2} en lugar de {0. , 2}

3

Esto debería funcionar para lo que estipuló. Que depende de la posición de la tarjeta salvaje y es una expresión regular anidada:

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

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

2

que he visto un montón de respuestas, pero ... tengo una nueva. Funciona para mí al menos. He agregado una nueva restricción. Los números de versión no pueden comenzar (mayor, menor o parche) con ceros seguidos por otros.

01.0.0 no es válida 1.0.0 es válida 10.0.10 es válida 1.0.0000 no es válida

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

Se basa en una anterior. Pero veo esta solución mejor ... para mí;)

¡¡¡Disfruta !!!

4

Mis 2 centavos: Tenía este escenario: tuve que analizar los números de versión de un literal de cadena. (Sé que esto es muy diferente de la pregunta original, pero buscar en Google para encontrar una versión para el número de versión mostró este hilo en la parte superior, así que agregando esta respuesta aquí)

Así que la cadena literal sería algo así como: " La versión de servicio 1.2.35.564 se está ejecutando! "

Tuve que analizar el 1.2.35.564 fuera de este literal. Siguiendo el ejemplo de @ajborley, mi expresión regular es el siguiente:

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

Un pequeño C# fragmento de probar esto se parece a continuación:

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   
} 
+0

Sé que estás describiendo una situación alternativa y caso, pero solo para completar: SemVer 'requiere' que la cadena de versión tenga el formato 'XYZ' (entonces, exactamente tres partes), donde X e Y deben ser enteros no negativos y sin ceros a la izquierda adicionales. Ver http://semver.org/. –

+1

@JochemSchulenklopper gracias, estoy al tanto de SemVer, aunque la pregunta no menciona nada sobre SemVer. – dotnetguy

+1

Es cierto. Un colega me hizo referencia a esta pregunta sobre el análisis de cadenas SemVer, de modo que enmarqué mi lectura de las respuestas. –

1

Uno más solución:

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

Especificación 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> 
1

Mi toma en esto, como un buen ejercicio - vparse, que tiene una tiny source, con una función 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; 
} 
1

que tenía la obligación de buscar/altura de los números de versión, que sigue la convención experto o incluso sólo un dígito. Pero no califica en ningún caso. Era peculiar, me tomó tiempo, entonces se me ocurrió esto:

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

Esto hace que la versión,

  1. empieza por un dígito
  2. puede tener cualquier número de dígitos
  3. Solo dígitos y '.' están permitidos

Un inconveniente es que la versión puede incluso terminar con '.' Pero puede manejar longitud indefinida de la versión (versiones loco si quieres llamarlo así)

Partidos:

  • 1.2.3
  • 1.09.5
  • 3.4.4.5.7.8.8 .
  • 23.6.209.234.3

Si no está satisfecho con ''. terminando, puede ser que pueda combinar con endswith lógica

+0

Para deshacerse del último dígito, tal vez le gustaría probar esto: '(\ d +) (. \ D +) *' – cassioso

Cuestiones relacionadas