Feedback

C# - Zahlen in Wörter umwandeln

Veröffentlicht von am 4/24/2015
(0 Bewertungen)
Hey Leute,

hier ist eine Funktion die Ziffern in Wörter umwandelt.

z.B. 10100102
ZEHNMILLIONENEINHUNDERTTAUSENDEINHUNDERTZWEI

Gebraucht wir das manchmal bei Rechnungen oder beim Scheckdruck.

string word = new NumberToWord().Convert(101101101001);


PS.: Unglaublich wie viele Ausnahmen es im Deutschen gibt.
public class NumberToWord
    {
        /// <summary>
        /// Konvertiert eine Zahl in Wörter
        /// </summary>
        /// <param name="number">Die Zahl die umgewandelt werden soll</param>
        /// <returns>Die Zahlen als Wörter</returns>
        internal string Convert(long number)
        {
            if (number == 0) return "nul";

            List<NumberBase> numberlist = new List<NumberBase>();
            NumberBase num = new Hundert();
            numberlist.Add(num);
            num.Calculate(number, numberlist);

            numberlist.Reverse();
            StringBuilder sb = new StringBuilder();
            foreach (NumberBase numberBase in numberlist)
            {
                sb.Append(numberBase);
                // sb.Append(" ");
            }
            return sb.ToString().ToUpper().Trim();
        }
    }

    public abstract class NumberBase
    {
        private static string[] EinerArray = {"", "ein", "zwei", "drei","vier","fünf","sechs","sieben","acht","neun","zehn","elf","zwölf"};
        private static string[] ZehnerArray ={"","zehn","zwanzig", "dreisig", "vierzig","fünfzig","sechzig","siebzig","achtzig","neunzig"};

        private long tausender = 0;

        private int hundert = 0;
        private int zehner = 0;
        private int einer = 0;

        public string Bezeichner { get; set; }

        protected NumberBase()
        {
            Bezeichner = this.GetType().Name;
        }
        
        /// <summary>
        /// Berechnet die Tausender, Hunderter, Zehner und Einer Stellen aus der Zahl
        /// </summary>
        /// <param name="number">Zahl</param>
        /// <param name="numberlist">Liste mit den Zahlen Objekten</param>
        internal void Calculate(long number, List<NumberBase> numberlist)
        {
            long num = number;
            
            tausender = RestValue(ref num, 1000);
            hundert = (int) RestValue(ref num, 100);
            zehner = (int)RestValue(ref num, 10);
            einer = (int)RestValue(ref num, 1);
            
            if (tausender < 1) return;
            NumberBase numbase = (NumberBase) Activator.CreateInstance(this.GetType().BaseType); // Typ der Eltern Klasse erstellen
            numberlist.Add(numbase);
            numbase.Calculate(tausender, numberlist);
        }

        /// <summary>
        /// Dividiert die Zahl und ermittelt den Restwert
        /// </summary>
        /// <param name="number"></param>
        /// <param name="teiler"></param>
        /// <returns></returns>
        private long RestValue(ref long number, int teiler)
        {
            long temp = number;
            long rest = temp % teiler;
            temp -= rest;
            temp /= teiler;
            number = rest;

            return temp;
        }

        /// <summary>
        /// Wandelt die Ziffern in Wörter um
        /// </summary>
        /// <returns></returns>
        private string ConvertToString() 
        {
            string output = string.Empty;

            if(hundert == 0 && zehner == 0 && einer == 0) return "";

            if (hundert > 0)
                output += EinerArray[hundert] + typeof(Hundert).Name; // Die Hunderter Ziffer
            
            if (zehner > 0)
            {
                if (zehner == 1)                            // Die Zehner Ziffer mit ihren Ausnahmen für 11, 12, 17
                {
                    string zehnerstring = string.Empty;

                    if (einer == 1 || einer == 2)                                          // Bei 11 und 12
                        zehnerstring += EinerArray[Convert.ToInt32("" + zehner + einer)];
                    else
                        zehnerstring += EinerArray[einer] + ZehnerArray[zehner];

                    if (einer == 7)
                        zehnerstring = zehnerstring.Replace("en", ""); // Bei Sieben das 'en' für Siebzehn löschen
                    output += zehnerstring;
                }
                else if (einer == 0) output += ZehnerArray[zehner];     // Wenn der Einer 0 ist dann nur der Zehner z.B. 20 = zwansig
                else
                    output += EinerArray[einer] + "und" + ZehnerArray[zehner]; // Alles über 19 und nicht 0 als Einer
            }
            else
            {
                if (this.GetType() == typeof(Hundert) && hundert == 0 && tausender > 0) output += "und";

                // Die Eins ist im Deutsch doch echt zum Kotzen Eine Millionen Ein Tausend Eins
                output += EinerArray[einer];                                     // Wenn der Zehner 0 ist muss der Einer alleine stehen
                
                if (einer == 1)// Wenn der Einer eine Eins ist :D
                {
                    if(hundert > 0 && zehner == 0 || this.GetType() == typeof(Hundert))
                        output += "s"; // TK

                    else if (this.GetType() != typeof(Tausend) || this.GetType() == typeof(Hundert)) output += "e";
                }
            }

            if (this.GetType() != typeof(Hundert)) output += Bezeichner;  // Den Bezeichner noch dazu, aber nur wenn es nicht Hundert ist

            return output.Trim();
        }

        public override string ToString()
        {
            return ConvertToString();
        }
    }

    #region NumberClasses

    public class Hundert : Tausend
    {
    }

    public class Tausend : Millionen
    {
    }

    public class Millionen : Milliarden
    {
    }

    public class Milliarden : Billionen
    {
    }

    public class Billionen : Billiarden
    {
    }

    public class Billiarden : Trillionen
    {
    }

    public class Trillionen : Trilliarden
    {
    }

    public class Trilliarden : NumberBase
    {
    }
    #endregion
Abgelegt unter Fibu, Ziffern, Zahlen, Wörter, Scheckdruck, Rechnungen.

3 Kommentare zum Snippet

Koopakiller schrieb am 4/24/2015:
Dein Code ist etwas seltsam strukturiert. Statt den vielen leeren Klassen deren Vererbung für mich keinen Sinn ergibt, könntest du die Verarbeitung auch in NumberBase verlagern und virtuelle Member implementieren.
Tango.Kilo schrieb am 4/25/2015:
Die Verarbeitung als auch die Ausgabe ist in NumberBase drin.
"etwas seltsam strukturiert"?
Es gibt ein Basis Klasse und über die einzelnen Generationen bekommt jeder Child diese Eigenschaften.
In Dot.net kann man eben nur von einem Parent Erben.
Hat aber auch wieder den Vorteil das es nur einen Basetyp gibt. Den ich benütze wenn ein Überhang in der Zahl existiert und so die nächst größere Einheit nötig wird. In dem ich ein Createinstanz des Parent Typs absetze.
So hat man gleich die richtige Bezeichnung für den Zahlenblck(z.B Millionen) dabei.


Statt der Vererbung hättest man das ganze auch rekursiv machen können. Dann hätten die einzelnen Bezeichnungen (Tausender, Millionen. Etc.) In ein Array oder ähnliches gepackt werden können. Aber dann hätte man bei jedem Durchlauf erst ermitteln müssen um welchen Zahlenblock es sich handelt.

Bei der Vererbung ist das nicht nötig, den das ergibt sich aus dem Typ automatisch.


Koopakiller schrieb am 4/25/2015:
Leere Klassen bringen nichts. Auch macht es logisch gesehen keinen Sinn das Hundert von Tausend erbt. Anders herum wäre ich zwar noch immer skeptisch ob man es der beste Weg ist, aber so würde es logisch mehr Sinn machen da 1000 einen Hunderter enthält.

Das die Verarbeitung in NumberBase steckt habe ich übersehen. Innerhalb von NumberBase aber auf die abgeleiteten Klassen mittels typeof(...) zuzugreifen ist das was mich wirklich stört. Wenn man unbedingt OOP hier hinein drücken will, sollte man den Namen der Zahl, die Anzahl der Stellen etc. in den Klassen Million, Milliarde usw. ablegen. Das meinte ich mit den virtuellen Membern.

Mit der Verarbeitung selbst habe ich kein Problem. Lediglich das unschön umgesetzte OOP stört mich. Stelle dir vor ich will einen Typ von NumberBase ableiten - die Basisklasse will ich deswegen aber nicht ändern müssen.
 

Logge dich ein, um hier zu kommentieren!