13 Stimmen

Regex, um schlampige Brüche / gemischte Zahlen abzugleichen

Ich habe eine Reihe von Texten, die gemischte Zahlen enthalten (d. h. einen ganzen und einen gebrochenen Teil). Das Problem ist, dass der Text voller menschlich kodierter Schlamperei ist:

  1. Der ganze Teil kann existieren oder nicht (z. B. "10")
  2. Der gebrochene Teil kann vorhanden sein oder nicht (z. B. "1/3")
  3. Die beiden Teile können durch Leerzeichen und/oder einen Bindestrich getrennt werden (z. B. "10 1/3", "10-1/3", "10 - 1/3").
  4. Der Bruch selbst kann Leerzeichen zwischen der Zahl und dem Schrägstrich enthalten oder nicht (z. B. "1 /3", "1/ 3", "1 / 3").
  5. Nach dem Bruch kann weiterer Text stehen, der ignoriert werden muss

Ich benötige eine Regex, die diese Elemente analysieren kann, damit ich aus diesem Durcheinander eine richtige Zahl erstellen kann.

11voto

Craig Walker Punkte 46757

Hier ist eine Regex, die alle Daten verarbeiten kann, die ich ihr vorlege:

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

Dadurch werden die Ziffern in die folgenden Gruppen eingeteilt:

  1. Der ganze Teil der gemischten Zahl, falls vorhanden
  2. Der Zähler, wenn ein Bruch vorliegt
  3. Der Nenner, falls ein Bruch existiert

Auch hier ist die RegexBuddy Erklärung für die Elemente (die mir ungemein geholfen, wenn es zu konstruieren):

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) «$»

2voto

Andru Luvisi Punkte 23151

Ich denke, es ist einfacher, die verschiedenen Fälle (vollständig gemischt, nur Bruch, nur Zahl) getrennt voneinander zu behandeln. Zum Beispiel:

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";

1voto

Brad Gilbert Punkte 33120

Wenn Sie Folgendes verwenden Perl 5.10 würde ich es so schreiben.

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

Dann können Sie auf die Werte aus der Datei %+ Variable wie diese:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X