2008-10-29 15 views
11

Tengo una serie de texto que contiene números mixtos (es decir, una parte completa y una parte fraccionaria). El problema es que el texto está llena de dejadez codificadas humano:Regex para hacer coincidir fracciones descuidadas/números mixtos

  1. puede o no puede existir Toda la parte (ex: "10")
  2. La parte fraccionaria puede o no existir (por ejemplo: " 1/3 ")
  3. Las dos partes pueden estar separadas por espacios y/o guiones (por ejemplo," 10 1/3 "," 10-1/3 "," 10 - 1/3 ").
  4. La fracción misma puede o no tener espacios entre el número y la barra inclinada (por ejemplo, "1/3", "1/3", "1/3").
  5. Puede haber otro texto después de la fracción que debe ser ignorado

Necesito una expresión regular que puede analizar estos elementos para que pueda crear un número apropiado de este lío.

+0

Ya tengo una expresión regular solución, y funciona muy bien, así que voy a compartirlo con TAN con la esperanza de que le ahorrará a alguien más mucho trabajo. –

+0

¿Qué lenguaje y/o motor de expresiones regulares es para? –

+0

Fue para Java, pero con RegexBuddy podría haberlo pasado fácilmente a cualquier número de motores. –

Respuesta

10

Aquí es una expresión regular que se encargará de todos los datos que pueda lanzar en él:

(\d++(?! */))? *-? *(?:(\d+) */ *(\d+))?.*$ 

Esto pondrá los dígitos en los siguientes grupos:

  1. Toda la parte del número mixto , si existe
  2. El numerador, si una fracción sale
  3. El denominador, si existe una fracción

Además, aquí es la explicación RegexBuddy de los elementos (que me ayudó enormemente cuando construirlo):

Match the regular expression below and capture its match into backreference number 1 «(\d++(?! */))?» 
    Between zero and one times, as many times as possible, giving back as needed (greedy) «?» 
    Match a single digit 0..9 «\d++» 
     Between one and unlimited times, as many times as possible, without giving back (possessive) «++» 
    Assert that it is impossible to match the regex below starting at this position (negative lookahead) «(?! */)» 
     Match the character “ ” literally « *» 
     Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*» 
     Match the character “/” literally «/» 
Match the character “ ” literally « *» 
    Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*» 
Match the character “-” literally «-?» 
    Between zero and one times, as many times as possible, giving back as needed (greedy) «?» 
Match the character “ ” literally « *» 
    Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*» 
Match the regular expression below «(?:(\d+) */ *(\d+))?» 
    Between zero and one times, as many times as possible, giving back as needed (greedy) «?» 
    Match the regular expression below and capture its match into backreference number 2 «(\d+)» 
     Match a single digit 0..9 «\d+» 
     Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+» 
    Match the character “ ” literally « *» 
     Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*» 
    Match the character “/” literally «/» 
    Match the character “ ” literally « *» 
     Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*» 
    Match the regular expression below and capture its match into backreference number 3 «(\d+)» 
     Match a single digit 0..9 «\d+» 
     Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+» 
Match any single character that is not a line break character «.*» 
    Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*» 
Assert position at the end of the string (or before the line break at the end of the string, if any) «$» 
+1

la parte '. * $' No tiene sentido, simplemente descarta lo que coincide. Solo elimínalo. Aparte de eso, no se ve tan mal. –

+0

Estaba buscando una solución similar y esta es la que terminó funcionando bien para mí: http://regexlib.com/REDetails.aspx?regexp_id=2127 – DavGarcia

2

Creo que puede ser más fácil para hacer frente a los diferentes casos (sólo lleno mezclado, fracción, sólo el número de) por separado el uno del otro. Por ejemplo:

sub parse_mixed { 
    my($mixed) = @_; 

    if($mixed =~ /^ *(\d+)[- ]+(\d+) *\/ *(\d)+(\D.*)?$/) { 
    return $1+$2/$3; 
    } elsif($mixed =~ /^ *(\d+) *\/ *(\d+)(\D.*)?$/) { 
    return $1/$2; 
    } elsif($mixed =~ /^ *(\d+)(\D.*)?$/) { 
    return $1; 
    } 
} 

print parse_mixed("10"), "\n"; 
print parse_mixed("1/3"), "\n"; 
print parse_mixed("1/3"), "\n"; 
print parse_mixed("10 1/3"), "\n"; 
print parse_mixed("10-1/3"), "\n"; 
print parse_mixed("10 - 1/3"), "\n"; 
1

Si está utilizando Perl 5.10, así es como lo escribiría.

 
m{ 
^
    \s*  # skip leading spaces 

    (?'whole' 
    \d++ 
    (?! \s*[\/]) # there should not be a slash immediately following a whole number 
) 

    \s* 

    (?: # the rest should fail or succeed as a group 

    -?  # ignore possible neg sign 
    \s* 

    (?'numerator' 
    \d+ 
    ) 

    \s* 
    [\/] 
    \s* 

    (?'denominator' 
    \d+ 
    ) 
)? 
}x 

A continuación, puede acceder a los valores de la variable %+ así:

$+{whole}; 
$+{numerator}; 
$+{denominator}; 
Cuestiones relacionadas