2008-10-29 5 views
11

J'ai une série de texte qui contient des nombres mélangés (c'est-à-dire: une partie entière et une partie fractionnaire). Le problème est que le texte est plein de laisser-aller code humain:Regex pour faire correspondre les fractions sloppy/nombres mélangés

  1. La partie entière peut ou peut ne pas exister (ex: « 10 »)
  2. La partie fractionnaire peut ou peut ne pas exister (ex: " 1/3 ")
  3. Les deux parties peuvent être séparées par des espaces et/ou des tirets (ex:" 10 1/3 "," 10-1/3 "," 10 - 1/3 ").
  4. La fraction elle-même peut avoir ou non des espaces entre le nombre et la barre oblique (ex: "1/3", "1/3", "1/3").
  5. Il peut y avoir un autre texte après la fraction qui doit être ignoré

je besoin d'un regex qui peut analyser ces éléments pour que je puisse créer un nombre approprié de ce gâchis.

+0

J'ai déjà une expression régulière de la solution, et il fonctionne très bien, donc je vais partager avec SO dans l'espoir que cela sauvera beaucoup de travail à quelqu'un d'autre. –

+0

Pour quelle langue et/ou moteur regex est-ce que c'est? –

+0

C'était pour Java, mais avec RegexBuddy, j'aurais pu facilement le faire tourner vers n'importe quel moteur. –

Répondre

10

est ici une expression régulière qui va gérer toutes les données que je peux jeter à elle:

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

Cela mettra les chiffres dans les groupes suivants:

  1. La partie entière du nombre mixte , si elle existe
  2. Le numérateur, si une fraction sort
  3. Le dénominateur, si une fraction existe

En outre, voici l'explication RegexBuddy pour les éléments (ce qui m'a énormément aidé lors de la construction il):

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 partie '. * $' Est inutile, elle jette juste ce qu'elle correspond. Retirez-le. A part ça, ça n'a pas l'air trop mal. –

+0

Je cherchais une solution similaire et c'est celle qui a bien fonctionné pour moi: http://regexlib.com/REDetails.aspx?regexp_id=2127 – DavGarcia

2

Je pense qu'il peut être plus facile d'aborder les différents cas (pleine mixte, fraction seulement, nombre seulement) séparément les uns des autres. Par exemple:

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 vous utilisez Perl 5.10, voici comment je l'écrire.

 
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 

Ensuite, vous pouvez accéder aux valeurs de la variable %+ comme ceci:

$+{whole}; 
$+{numerator}; 
$+{denominator}; 
Questions connexes