Code:
Public Function NumberWord(n As Integer) As String
Select Case n
Case Is < Integer.MinValue + 2, Integer.MaxValue
Throw New ArgumentOutOfRangeException("n", n, String.Format("valid range is: -{0} to {0}", Integer.MaxValue - 1))
Case 0 : Return "null"
Case Is < 0 : Return "minus " & NumberWord(-n)
End Select
NumberWord = NumberWordRecursive(n)
Dim zehner, einer As Integer
zehner = Math.DivRem(n, 10, einer)
' "s" anhängen, wenn keine zehner-stelle, und einer = 1 - zB 1, 101, 1001, 2501 ...
If (zehner Mod 10) = 0 AndAlso einer = 1 Then NumberWord &= "s"
End Function
Private Function NumberWordRecursive(n As Integer) As String
Dim einer = "##invalid ein zwei drei vier fünf sechs sieben acht neun zehn elf zwölf".Split
Dim zehner = "##invalid zehn zwanzig dreißig vierzig fünfzig sechzig siebzig achzig neunzig".Split
Dim dimension = {New With {.n = 100, .text = "##invalid"},
New With {.n = 1000, .text = "hundert"},
New With {.n = 1000000, .text = "tausend"},
New With {.n = 1000000000, .text = "millionen"},
New With {.n = Integer.MaxValue, .text = "milliarden"}}
Dim geteilt, rest As Integer
geteilt = Math.DivRem(n, 10, rest)
Select Case n
Case 0 : Return ""
Case Is < 13 : Return einer(n)
Case Is < 20 : Return einer(rest) & zehner(1)
Case Is < 100
If rest = 0 Then Return zehner(geteilt)
'wenn Einer da sind, vertauschte Reihenfolge
Return einer(rest) & "und" & zehner(geteilt)
Case Else
Dim i = Array.FindIndex(dimension, Function(itm) n < itm.n)
geteilt = Math.DivRem(n, dimension(i - 1).n, rest)
NumberWordRecursive = NumberWordRecursive(geteilt) & dimension(i).text & NumberWordRecursive(rest)
If i > 2 AndAlso geteilt = 1 Then
Select Case i 'singular bei millionen/milliarden
Case 3
NumberWordRecursive = NumberWordRecursive.Remove(10, 2).Insert(3, "e")
Case 4
NumberWordRecursive = NumberWordRecursive.Remove(12, 1).Insert(3, "e")
End Select
End If
End Select
End Function
Private Sub Test()
' paar interessante Zahlen
For Each i In {0, 1, 125, 1000000, 1000000000, 1000100000, 100010000, 101001, 1021, 3333, 1123456, 3123456, Integer.MaxValue - 1}
Console.WriteLine(NumberWord(i))
Next
End
End Sub
Ich denke aber, das sollte man eher als abgetrennte Funktion angehen. Etwa Runden hat ja nichts mit Zahlworten zu tun, und wenn man tatsächlich die Cents auf dem Scheck aufführen will, kann man die 2 Nachkommastellen ja nochmal extra durchlaufen lassen.
Am Algo-Konzept hat mich überrascht, wie systematisch die Grammatik eiglich ist, und insbesondere rekursiv: Die Regeln für milliardener, millionener, tausender, hunderter sind immer dieselben.
Und das unregelmäßige Zeugs im Bereich unter hundert wiederholt sich - absolut regelmäßig - auf den höheren Ebenen - Rekursion eben.
ZB um die Zahl 125125125 zu "sagen", wird das Wort "einhundertfünfundzwanzig" drei mal gebildet, und es wird halt "millionen" und "tausend" eingefügt:
einhundertfünfundzwanzigmillioneneinhundertfünfund zwanzigtausendeinhundertfünfundzwanzig
Technisch gesehen setze ich v.a. 2 String-Arrays ein, und ein Array von anonymen Datentypen.
Auch verwende ich die weniger bekannte Math.DivRem() - Methode, eine Art mächtigeres Modulo, weils den Rest der Teilung nicht verfallen lässt.
Und Array.FindIndex() mit anonymer Methode - kennen vmtl. auch nur Leute, die gelegentlich browsen im Framework - mittm ObjectBrowser
Quelle: vb-paradise.de







