Boris Hoeller: Übersetzungsroutine

Bittere, aber nicht uninterssante Aufgabe :

#!/usr/bin/perl

$betrag = <STDIN> # Eingabe z.B. "54.998,00"

(hier arbeitet jetzt das gesuchte Script:))))

print "$betrag lautet übersetzt in Worte $betrag_in_worten"; # Ausgabe dann "54.998,00 lautet übersetzt in Worte vierundfünfzigtausendneunhundertachtundneunzig"

Schulbeispiele, Erfahrungen, Ideen, Verweise oder ähnliches?

Vielen Dank für jegliche Info und Grüße aus Bonn
Bo

  1. Bis zu welcher hoehe sollen die betraege gehen?

    moeglich waere eine routine, welche den betrag von hinten her aufsplitted

    zuerst die letzten beiden stellen hinter dem komma, dann die 3 bis zum ersten punkt vor dem komma...
    usw. von hinten nach vorne.

    dann die einzelnen wieder splitten und in worte konvertieren. das waere ne moeglichkeit. wenn es aber alle zahlen bis ins unendlich konvertieren soll wird es ein wenig groesser.

    eine routine die das automatisch erledigt ist mir leider nicht bekannt.

    cu

    1. zuerst die letzten beiden stellen hinter dem komma, dann die 3 bis zum ersten punkt vor dem komma...
      usw. von hinten nach vorne.

      dann die einzelnen wieder splitten und in worte konvertieren. das waere ne moeglichkeit. wenn es aber alle zahlen bis ins unendlich konvertieren soll wird es ein wenig groesser.

      Ich wuerde es wohl auch so machen. Ist sicher garnicht so schwer. Die Dreierbloecke kann kan ja mit einer Subroutine uebersetzen.
      Allerdings ist das mit dem Unendlich auch nicht so dramatisch, denn irgendwann kannst DU eine Zahl nichtmehr mit Worten bezeichnen, oder weisst du was hinter der quadrilliarde kommt ?

      Viele gruesse, Thomas Hieck

      1. hye :-)

        Allerdings ist das mit dem Unendlich auch nicht so dramatisch, denn irgendwann kannst DU eine Zahl nichtmehr mit Worten bezeichnen, oder weisst du was hinter der quadrilliarde kommt ?

        hinter der quadrilliarde kommt die
        FANTASTILLION UND DANN DIE FANTASTILLIARDE :-)
        so stehts im Lustigen Taschenbuch :-)
        soviel (und noch mehr) hat nämlich Dagobert Duck

        ok, cu

      2. hi!

        Allerdings ist das mit dem Unendlich auch nicht so dramatisch, denn irgendwann kannst DU
        eine Zahl nichtmehr mit Worten bezeichnen, oder weisst du was hinter der quadrilliarde
        kommt ?

        Quintillion :)

        bye, Frank!

      3. Hi,

        Ich wuerde es wohl auch so machen. Ist sicher garnicht so schwer. Die Dreierbloecke kann kan ja mit einer Subroutine uebersetzen.
        Allerdings ist das mit dem Unendlich auch nicht so dramatisch, denn irgendwann kannst DU eine Zahl nichtmehr mit Worten bezeichnen, oder weisst du was hinter der quadrilliarde kommt ?

        das Thema hatten wir gerade erst in de.sci.mathematik... :-)

        Teile die Zahl in sechser-Blöcke auf. Der nullte Block sind die Tausender etc., danach folgen:

        Mill (ionen, iarden),
        Bill*,
        Trill*,
        Quadrill*,
        Quintill*,
        Sextill*,
        Septill*,
        Octill*,
        Nonill*,
        Dezill*,
        usw. (hoffe, ich habe jetzt keinen Fehler gemacht...). Wenn es noch höher gehen soll: Eine Dezillion sind 10^(6*10), wobei die 10 in der Klammer sich in "Dez" ausdrückt. 10^100 hat der Sohn (Enkel?) Eines Kindes mal benamst, und zwar als "gogool", was überraschend simpel ist für eine Zahl, die 20 Stellen mehr hat als die Zahl der Atome im bekannten Universum. Eine Zahl mit 10^100 Ziffern (also 10^(10^100)) heißt übrigens gogoolplex... :-)

        Du kannst also in etwa folgendermaßen vorgehen:
        $zahl = "123.456.789,12";
        $block = 0; # aktueller 6er-Block
        @bname = ('','Mill','Bill','Trill',...);
        if ($zahl =~ /^(.*),(.*)$/) { $zahl = $1; $komma = $2; } # Ganzzahl herstellen, Nachkommastellen speichern
        $zahl =~ s/.//g; # Punkte entfernen
        $string = &num2string($zahl,$block);

        Jetzt brauchst Du nur noch die rekursive sub num2string, die folgendes macht:

        • Die Zahl aufspalten in /^(.*)(.{6})$/, falls mehr als 6 Ziffern.
        • Die letzten 6 Ziffern "manuell" in ein Wort aufteilen (vielleicht mit zweimaligem Aufruf einer Funktion, die eine Zahl < 1000 in ein Wort umwandelt).
        • Dabei muß $bname[$block] + 'ionen' bzw. 'iarden' addiert werden, bzw. 'tausend', falls $bname[$block] eq '' und die höherwertigen drei Ziffern bearbeitet werden.
        • Nun muß nur noch "return &num2string(Restzahl,$block+1) . Ergebnis dieser Rekursion" ausgegeben werden.

        Die Funktion num2string überlasse ich Dir ;-)

        Cheatah

        1. das Thema hatten wir gerade erst in de.sci.mathematik... :-)

          Teile die Zahl in sechser-Blöcke auf. Der nullte Block sind die Tausender etc., danach folgen:

          Mill (ionen, iarden),
          Bill*,
          Trill*,
          Quadrill*,
          Quintill*,
          Sextill*,
          Septill*,
          Octill*,
          Nonill*,
          Dezill*,
          usw. (hoffe, ich habe jetzt keinen Fehler gemacht...).

          Als Noch-Mathematikstudent hab ich auch keine Probleme die Reihe fortzufuehren :-) Aber das war rein rethorisch gefragt, da die meisten doch sowieso die Quadrilliarde als letzte Zahl kennen.
          Uebrigens wird im allgemeinen Sprachgebrauch nicht mehr als Milliarde benutzt. Das naechstehoehere bezeichnet man dann schon als Tausend-Milliarde. Vielleicht der Vorstellung wegen.

          Wenn es noch höher gehen soll: Eine Dezillion sind 10^(6*10), wobei die 10 in der Klammer sich in "Dez" ausdrückt. 10^100 hat der Sohn (Enkel?) Eines Kindes mal benamst, und zwar als "gogool", was überraschend simpel ist für eine Zahl, die 20 Stellen mehr hat als die Zahl der Atome im bekannten Universum.

          :-) GOGOOL klingt wirklich cool. Ich glaub das mit den Atomen war sogar untertrieben. Meines Wissen waren sogar Teilchen. Aber es reicht wohl, wenn es Uununununvorstellbar ist :-)

          Eine Zahl mit 10^100 Ziffern (also 10^(10^100)) heißt übrigens gogoolplex... :-)

          <menschelei>Klingt fast noch schoener :-) Sollten wir nicht gleich wieder aus dem Sprachgebrauch des Forums verbannen :-)</menschelei>

          Die Funktion num2string überlasse ich Dir ;-)

          Mir nicht, aber Boris machts sicher zuende. Hab gerade garkein Nerv dafuer.

          Viele Gruesse, Thomas Hieck

          1. Hi,

            Als Noch-Mathematikstudent hab ich auch keine Probleme die Reihe fortzufuehren :-) Aber das war rein rethorisch gefragt, da die meisten doch sowieso die Quadrilliarde als letzte Zahl kennen.

            :-)

            Uebrigens wird im allgemeinen Sprachgebrauch nicht mehr als Milliarde benutzt. Das naechstehoehere bezeichnet man dann schon als Tausend-Milliarde. Vielleicht der Vorstellung wegen.

            Nun ja, die Erfahrung habe ich noch nicht gemacht, daß jemand "tausend Milliarden" sagt... zumal unsereiner natürlich weiß, daß Bill Gates "billionair" (nicht zu verwechseln mit dem Billionär *g*) ist :-)

            :-) GOGOOL klingt wirklich cool. Ich glaub das mit den Atomen war sogar untertrieben. Meines Wissen waren sogar Teilchen. Aber es reicht wohl, wenn es Uununununvorstellbar ist :-)

            Du magst recht haben, ich habe auch einen Moment überlegt. Letztlich ist es aber für uns uninteressant, zumal ich in diesem Zusammenhang öfter den Bereich "10^80 bis 10^81" höre... und das deckt ungefähr den Unterschied Atome <-> Teilchen ab ;-)

            Eine Zahl mit 10^100 Ziffern (also 10^(10^100)) heißt übrigens gogoolplex... :-)

            <menschelei>Klingt fast noch schoener :-) Sollten wir nicht gleich wieder aus dem Sprachgebrauch des Forums verbannen :-)</menschelei>

            Finde ich auch :-)
            Dieses Forum kann ich übrigens zu GOGOOLPLEX Prozent empfehlen ;-)))

            Die Funktion num2string überlasse ich Dir ;-)

            Mir nicht, aber Boris machts sicher zuende. Hab gerade garkein Nerv dafuer.

            Ging mir genauso ;-) vor allem, weil es ein bißchen mit Ausprobieren ist.

            Cheatah

  2. Hallo Boris,

    das ist mal wieder so ein Thread - klare Aufgabenstellung, keine klaren Antworten. Weil mich das Thema interessiert hat, hab ich mal eine Routine geschrieben, die so was macht. Leider hat sie bei Zahlen ueber 100000 noch einen Fehler, deshalb bin ich da noch nicht weitergekommen. Auch ist sie ziemlich umstaendlich programmiert - aber im Gegensatz zu den bisherigen Antworten wenigstens eine Diskussionsgrundlage <g>. Also:

    #!/usr/bin/perl

    Scriptname: betrag.pl

    Scriptaufruf in URL-Zeile (Beispiel): http://127.0.0.1/cgi-bin/betrag.pl?71234,00

    $Zahl = $ENV{'QUERY_STRING'};
    $x = &Ganzzahl_in_Worten($Zahl);

    Test (HTML-Output / CGI)

    print "Content-type: text/html\n\n";
    print "<html><body>\n";
    print "<font size=5>$Zahl = $x</font>\n";
    print "</body></html>\n";

    sub Ganzzahl_in_Worten
    {
      @EElems = ("","eins","zwei","drei","vier","fünf","sechs","sieben","acht","neun","zehn","elf","zwölf","dreizehn","vierzehn","fünfzehn","sechzehn","siebzehn","achtzehn","neunzehn","null");
      @ZElems = ("und","ein","zwanzig","dreißig","vierzig","fünfzig","sechzig","siebzig","achtzig","neunzig","hundert");
      @PElems = ("ein","hundert","tausend","millionen");
      $Input = $_[0];
      $Input =~ s/.//g;
      ($Zahl,$Rest) = split(/,/,$Input);
      if($Zahl < 10) {
        if($Zahl == 0) {
          $Output = $EElems[-1];
          return($Output);
    }
    else {
          $Output = $EElems[$Zahl];
          return($Output);
    }
      }
      $Output = &Zehner($Zahl);
      if($Zahl < 100) {
        return($Output);
      }
      $Hunderter = substr($Zahl,length($Zahl)-3,1);
      if($Hunderter == 1) {
        $Output = $PElems[0].$PElems[1].$Output;
      }
      else {
        $Output = $EElems[$Hunderter].$PElems[1].$Output;
      }
      if($Zahl < 1000) {
        return($Output);
      }
      if($Zahl % 1000 == 0) {
    $Output = "";
      }
      if($Zahl < 10000) {
        $Tausender = substr($Zahl,length($Zahl)-4,1);
        if($Tausender == 1) {
          $Output = $PElems[0].$PElems[2].$Output;
          return($Output);
        }
        else {
          $Output = $EElems[$Tausender].$PElems[2].$Output;
          return($Output);
        }
      }
      $ZTausender = substr($Zahl,length($Zahl)-5,2);
      $Zehntausender = &Zehner($ZTausender);
      if($Zahl < 100000) {
        $Output = $Zehntausender.$PElems[2].$Output;
        return($Output);
      }
      if($Zahl < 1000000) {
        $HTausender = substr($Zahl,length($Zahl)-6,1);
        if($HTausender == 1) {
       # HIER FOLGT DER FEHLER: $Zehntausender WIRD PLOETZLICH FALSCH INTERPRETIERT
       $Output = $PElems[0].$PElems[1].$Zehntausender.$PElems[2].$Output;
          return($Output);
        }
        else {
          $Output = $EElems[$HTausender].$PElems[1].$Zehntausender.$PElems[2].$Output;
          return($Output);
        }
      }
      return($Output);
    }

    sub Zehner {
      $ZInput = $_[0];
      if($ZInput > 9 && $ZInput < 20) {
        $Result = $EElems[$ZInput];
    return($Result);
      }
      else {
        $Einer = substr($Input,length($ZInput)-1,1);
        $Zehner = substr($Input,length($ZInput)-2,1);
        if($Einer == 1) {
          $Result = $ZElems[1].$ZElems[0].$ZElems[$Zehner];
    }
        elsif($Zehner == 0) {
          $Result = $EElems[$Einer];
    }
        else {
          $Result = $EElems[$Einer].$ZElems[0].$ZElems[$Zehner];
    }
      }
      return($Result);
    }

    END

    Probier das mal mit Betraegen unter 100000 - dann sollte es prima funktionieren. Bei Betraegen zwischen 100000 und 1000000 verheddert er sich ploetzlich mit den Zehntausendern - obwohl die vorher bereits rechtzeitig in einer Variablen gespeichert wurden. Ich kam da einfach nicht weiter - vielleicht ist es ein Integer-Ueberlaufproblem oder so was.

    viele Gruesse
      Stefan Muenz

    1. Hallo Boris,

      das ist mal wieder so ein Thread - klare Aufgabenstellung, keine klaren Antworten. Weil mich das Thema interessiert hat, hab ich mal eine Routine geschrieben, die so was macht. Leider hat sie bei Zahlen ueber 100000 noch einen Fehler, deshalb bin ich da noch nicht weitergekommen. Auch ist sie ziemlich umstaendlich programmiert - aber im Gegensatz zu den bisherigen Antworten wenigstens eine Diskussionsgrundlage <g>. Also:

      Hallo Stefan, hallo an dieser Programmierung Interessierte,

      danke erstmal für die Grundlage auf meine Frage hin :).
      Hintergrund ist der Versuch, die automatische Generierung eines Wechselformulars zu programmieren.
      Der Wechsel wird im kaufmännischen Verkehr verwendet, aber ist sehr formstreng (verliert daher an Bedeutung, wegen der Angst einen Fehler zu machen ...) - ist aber aus kaufmännischer Sicht ein gutes Instrumetarium.  Die (rechtssichere ;) Erstellung eines
      Wechsels über HTML/P.E.R.L. könnte zu einer kleinen
      Wiedergeburt führen, da auch so im Zweifel z.B. kleine GmbH's über ihre Haftungssumme hinaus beliefert werden können, wenn der Bezogene eine natürliche, damit prinzipiell nicht haftungsbeschränkte Person ist.
      Nach der Formstrenge ist die Wechselsumme in Ziffern und Buchstaben anzugeben. Das zum Hintergrund.

      Auf der anderen Seite sind unter "modularen" Gesichtspunkten andere Anwendungsfelder für die 'Übersetzung' nicht ausgeschlossen.
      Auf jeden Fall interessant ;)

      Ich versuche jetzt mal das script zu verstehen, was mir aber bislang auffällt ist:

      Bislang 'packt' das Script auch unter 100.000,00 die 11-20er nicht richtig. Mehr dazu im nächsten posting ...

      Bis dann
      CU
      Bo

      #!/usr/bin/perl

      Scriptname: betrag.pl

      Scriptaufruf in URL-Zeile (Beispiel): http://127.0.0.1/cgi-bin/betrag.pl?71234,00

      $Zahl = $ENV{'QUERY_STRING'};
      $x = &Ganzzahl_in_Worten($Zahl);

      Test (HTML-Output / CGI)

      print "Content-type: text/html\n\n";
      print "<html><body>\n";
      print "<font size=5>$Zahl = $x</font>\n";
      print "</body></html>\n";

      sub Ganzzahl_in_Worten
      {
        @EElems = ("","eins","zwei","drei","vier","fünf","sechs","sieben","acht","neun","zehn","elf","zwölf","dreizehn","vierzehn","fünfzehn","sechzehn","siebzehn","achtzehn","neunzehn","null");
        @ZElems = ("und","ein","zwanzig","dreißig","vierzig","fünfzig","sechzig","siebzig","achtzig","neunzig","hundert");
        @PElems = ("ein","hundert","tausend","millionen");
        $Input = $_[0];
        $Input =~ s/.//g;
        ($Zahl,$Rest) = split(/,/,$Input);
        if($Zahl < 10) {
          if($Zahl == 0) {
            $Output = $EElems[-1];
            return($Output);

      »»  }
      »»  else {

      $Output = $EElems[$Zahl];
            return($Output);

      »»  }

      }
        $Output = &Zehner($Zahl);
        if($Zahl < 100) {
          return($Output);
        }
        $Hunderter = substr($Zahl,length($Zahl)-3,1);
        if($Hunderter == 1) {
          $Output = $PElems[0].$PElems[1].$Output;
        }
        else {
          $Output = $EElems[$Hunderter].$PElems[1].$Output;
        }
        if($Zahl < 1000) {
          return($Output);
        }
        if($Zahl % 1000 == 0) {

      »»  $Output = "";

      }
        if($Zahl < 10000) {
          $Tausender = substr($Zahl,length($Zahl)-4,1);
          if($Tausender == 1) {
            $Output = $PElems[0].$PElems[2].$Output;
            return($Output);
          }
          else {
            $Output = $EElems[$Tausender].$PElems[2].$Output;
            return($Output);
          }
        }
        $ZTausender = substr($Zahl,length($Zahl)-5,2);
        $Zehntausender = &Zehner($ZTausender);
        if($Zahl < 100000) {
          $Output = $Zehntausender.$PElems[2].$Output;
          return($Output);
        }
        if($Zahl < 1000000) {
          $HTausender = substr($Zahl,length($Zahl)-6,1);
          if($HTausender == 1) {
         # HIER FOLGT DER FEHLER: $Zehntausender WIRD PLOETZLICH FALSCH INTERPRETIERT
         $Output = $PElems[0].$PElems[1].$Zehntausender.$PElems[2].$Output;
            return($Output);
          }
          else {
            $Output = $EElems[$HTausender].$PElems[1].$Zehntausender.$PElems[2].$Output;
            return($Output);
          }
        }
        return($Output);
      }

      sub Zehner {
        $ZInput = $_[0];
        if($ZInput > 9 && $ZInput < 20) {
          $Result = $EElems[$ZInput];

      »»  return($Result);

      }
        else {
          $Einer = substr($Input,length($ZInput)-1,1);
          $Zehner = substr($Input,length($ZInput)-2,1);
          if($Einer == 1) {
            $Result = $ZElems[1].$ZElems[0].$ZElems[$Zehner];

      »»  }

      elsif($Zehner == 0) {
            $Result = $EElems[$Einer];

      »»  }

      else {
            $Result = $EElems[$Einer].$ZElems[0].$ZElems[$Zehner];

      »»  }

      }
        return($Result);
      }

      END

      Probier das mal mit Betraegen unter 100000 - dann sollte es prima funktionieren. Bei Betraegen zwischen 100000 und 1000000 verheddert er sich ploetzlich mit den Zehntausendern - obwohl die vorher bereits rechtzeitig in einer Variablen gespeichert wurden. Ich kam da einfach nicht weiter - vielleicht ist es ein Integer-Ueberlaufproblem oder so was.

      viele Gruesse
        Stefan Muenz

    2. Hallo zusammen,

      habe mal in VB für Excel eine solche Funktion geschrieben, die der obigen Übersetzung stark ähnelt :)
      Der Zehntausenderfehler ist dort an sich ausgebügelt, wenn es trotzdem irgendwo hakt meldet euch nochmal, Ihr müßt sie jetzt einfach nochmal übersetzen.

      Function Zahlwort(zahl)
      Dim a, b, c, hunderter(5) As String, zehner(5) As String, hilfezahl(3)
      a = Array("null", "ein", "zwei", "drei", "vier", "fünf", "sechs", "sieben", "acht", "neun", "zehn", _
      "elf", "zwölf", "dreizehn", "vierzehn", "fünfzehn", "sechzehn", "siebzehn", "achtzehn", "neunzehn")
      b = Array("zehn", "zwanzig", "dreißzig", "vierzig", "fünfzig", "sechszig", "siebzig", "achtzig", "neunzig")
      c = Array("null", "eins", "zwei", "drei", "vier", "fünf", "sechs", "sieben", "acht", "neun")
      x = 1
      y = 1

      If zahl < 0 Then
      vorzeichen = "minus"
      zahl = Right(zahl, Len(zahl) - 1)
      End If
      If zahl Like "*,*" Then
      komzahl = Right(zahl, Len(zahl) - InStr(zahl, ","))
      For h = 1 To Len(komzahl)
      nachkom = nachkom & c(Left(komzahl, 1))
      komzahl = Right(komzahl, Len(komzahl) - 1)
      Next h
      komma = "komma"
      zahl = Fix(zahl)
      End If
      If zahl = 0 Then
      Zahlwort = vorzeichen & "null" & komma & nachkom
      Exit Function
      End If
      If zahl = 1 Then
      Zahlwort = vorzeichen & "eins" & komma & nachkom
      Exit Function
      End If
      If zahl > 999 And zahl < 1000000 Then
      tausend = "tausend"
      hilfezahl(2) = Right(zahl, 3)
      If hilfezahl(2) < 100 Then hilfezahl(2) = CInt(hilfezahl(2))
      zahl = Left(zahl, Len(zahl) - 3)
      x = 3
      y = 2
      End If
      If zahl > 999999 Then
      Millionen = "million"
      hilfezahl(1) = Right(zahl, 6)
      hilfezahl(2) = Right(zahl, 3)
      If hilfezahl(2) < 100 Then hilfezahl(2) = CInt(hilfezahl(2))
      zahl = Left(zahl, Len(zahl) - 6)
      If zahl <> 1 Then Millionen = "millionen"
      x = 3
      End If

      For i = y To x
      If Len(zahl) > 3 Then
          zahl = Left(zahl, 3)
          If zahl < 100 Then zahl = CInt(zahl)
          If zahl <> 0 Then tausend = "tausend"
      End If
      If zahl > 99 And Right(zahl, 2) = 0 Then
          hunderter(i) = a(Left(zahl, 1)) & "hundert"
      End If
      If zahl > 99 Then
          hunderter(i) = a(Left(zahl, 1)) & "hundert"
          zahl = zahl - Left(zahl, 1) * 100
      End If
      If zahl < 100 And zahl > 19 And Right(zahl, 2) > 0 Then
          If Right(zahl, 1) = 0 Then
              zehner(i) = b(Left(zahl, 1) - 1)
          Else
              zehner(i) = "und" & b(Left(zahl, 1) - 1)
              zahl = zahl - Left(zahl, 1) * 10
          End If
      End If
      If zahl < 20 And zahl > 0 Then
          zehner(i) = a(zahl) & zehner(i)
      End If
      zahl = hilfezahl(i)

      Next i

      Zahlwort = vorzeichen & hunderter(1) & zehner(1) & Millionen & hunderter(2) & zehner(2) & tausend & hunderter(3) & zehner(3) & komma & nachkom
      If komma <> "" Then
          If Right(Left(Zahlwort, InStr(Zahlwort, "k") - 1), 3) = "ein" Then Zahlwort = Left(Zahlwort, InStr(Zahlwort, "k") - 4) & "undeins" & Right(Zahlwort, Len(Zahlwort) - InStr(Zahlwort, "k") + 1)
      ElseIf komma = "" Then
          If Right(Zahlwort, 3) = "ein" Then Zahlwort = Left(Zahlwort, Len(Zahlwort) - 3) & "undeins"
      End If
      If Right(Left(Zahlwort, 4 + Len(vorzeichen)), 4) = "einm" Then Zahlwort = vorzeichen & "einem" & Right(Zahlwort, Len(Zahlwort) - 4 - Len(vorzeichen))

      End Function

      Gruß
      Holger