Bedragen en getallen omzetten naar uitgeschreven tekst in C#

Bedragen en getallen omzetten naar uitgeschreven tekst in C#.

Onlangs was het nodig om een functie te schrijven om bedrag uit te schijven in tekst. Hiervan zijn niet heel veel voorbeelden te vinden op het internet en zelf ben ik voor C# nog geen voorbeelden tegengekomen. Het leek het mij daarom zinvol om mijn implementatie te delen.

Er zijn diverse artikelen te vinden over hoe getallen uitgeschreven kunnen worden, maar eenduidige specificatie met alle mogelijke uitzonderingen heb ik op het moment van schrijven nog niet kunnen vinden. Ik heb mijn implementatie gebaseerd op enkele online bronnen met richtlijnen voor het uitschrijven van getallen.

·         https://onzetaal.nl/taaladvies/advies/getallen-uitschrijven

·         http://taaladvies.net/taal/advies/tekst/44

·         http://woordenlijst.org/leidraad/6/9

Er zijn voor sommige gevallen verschillende manieren waarom een getal kan worden uitgeschreven. Ik heb gekozen om een zo eenvoudig mogelijke implementatie te maken.  Het optionele woord 'en' word niet gebruikt. ('duizend een' i.p.v. het alternatieve 'duizend en een'), en 2500 wordt gewoon als 'tweeduizend vijfhonderd' geschreven i.p.v. het alternatief 'vijfentwintighonderd'.

Het implementeren van deze functionaliteit was meteen een leuke oefening om enkele moderne language features van C# toe te passen. Onderstaande code fragment illustreert een paar van deze features, die ik daaronder zal behandelen.

 

private static string BedragTekst(this decimal bedrag) =>

  bedrag.HeeftCenten() ?

    $"{bedrag.Euro().Tekst()} euro en {bedrag.Eurocent().Tekst()} cent" :

    $"{bedrag.Euro().Tekst()} euro";

 

 

Feature 1: Extension methods

Extension methods maken het mogelijk het aanroepen van functies te ‘chainen’. Er onstaat dan een vloeiend leesbaar statement i.p.v. dat functieaanroepen genest worden en eigenlijk omgekeerd gelezen moeten worden.

 

// Met extension methods

return bedrag.Euro().Tekst();

 

 

 

// Zonder extension methods

return Tekst(Euro(bedrag));

 

 

Feature 2: Korte notatie van functies die uit één statement bestaan.

Ik houd ervan om logica en berekeningen in (hele) kleine stukken op te knippen en aan ieder onderdeel een beschrijvende naam te geven. Door bij complexere berekeningen, business rules, condities deze kleinere stukken te hergebruiken, ontstaan er dan geen lange onleesbare formules, die je iedere keer moet ontcijferen wanneer je je code terug leest.

Als je veel van deze compacte business rules, condities of berekeningen hebt is het prettig om deze compact op te kunnen schijven zonder function body die onevenredig veel 'clutter' met zich mee brengt.

 

// Korte notatie

private static int Eenheden(this int getal) => getal % 10;

private static int Tientallen(this int getal) => (getal / 10) % 10;

private static int Honderdtallen(this int getal) => (getal / 100) % 10;

 

 

 

// Normale function body

private static int Eenheden(this int getal)

{

  return getal % 10;

}

private static int Tientallen(this int getal)

{

  return (getal / 10) % 10;

}

 

private static int Honderdtallen(this int getal)

{

  return (getal / 100) % 10;

}

 

 

 

Feature 3: Alternatieve schrijfwijze voor string.Format()

Er is tegenwoordig een alternatieve manier om een string op te bouwen t.o.v. String.Format. Door een ‘$’ voor een string te zetten kunnen expressies rechtstreeks tussen de accolades in de string worden geplaatst.

Naast dat deze opbouw prettiger leest dan de 'oude' manier, is het niet meer nodig om de volgorde van de placeholders en parameters in sync te houden, wat wel eens mis kan gaan bij het herformuleren van teksten met veel parameters.

 

 

// Alternatieve schrijfwijze voor string.Format

$"Ongeldige invoer: {bedrag}, bedrag moet groter zijn dan 0";

 

 

 

// Normale schrijfwijze string.Format

string.Format("Ongeldige invoer: {0}, bedrag moet groter zijn dan 0", bedrag);

 

 

Implementatie

Beperkingen

De code werkt voor getallen tussen 0 en Int32.MaxValue (2.147.483.647). Biljoenen komen dus niet aan bod.

Broncode voor het omzetten van bedragen en getallen naar uitgeschreven tekst in C#

using System;

 

namespace Dataleaf.Shared.Extensions

{

  public static partial class NumberExtensions

  {

    #region Vaste teksten

    private static string Nul = "nul";

    private static string Honderd = "honderd";

    private static string Duizend = "duizend";

    private static string Miljoen = "miljoen";

    private static string Miljard = "miljard";

 

    private static string[] EenhedenTot20 = new string[]

    {

      "",

      "een",

      "twee",

      "drie",

      "vier",

      "vijf",

      "zes",

      "zeven",

      "acht",

      "negen",

      "tien",

      "elf",

      "twaalf",

      "dertien",

      "veertien",

      "vijftien",

      "zestien",

      "zeventien",

      "achtien",

      "negentien",

    };

 

    private static string[] EenhedenVanaf20 = new string[]

    {

      "",

      "eenen",

      "tweeën",

      "drieën",

      "vieren",

      "vijfen",

      "zesen",

      "zevenen",

      "achten",

      "negenen",

    };

 

    private static string[] TientallenVanaf20 = new string[]

    {

      "twintig",

      "dertig",

      "veertig",

      "vijftig",

      "zestig",

      "zeventig",

      "tachtig",

      "negentig"

    };

    #endregion

 

    #region Berekeningen

    private static int Eenheden(this int getal) => getal % 10;

    private static int Tientallen(this int getal) => (getal / 10) % 10;

    private static int Honderdtallen(this int getal) => (getal / 100) % 10;

    private static int Duizendtallen(this int getal) => (getal / 1000) % 1000;

    private static int Miljoentallen(this int getal) => (getal / 1000000) % 1000;

    private static int Miljardtallen(this int getal) => (getal / 1000000000) % 1000;

    private static int Euro(this decimal bedrag) => (int)bedrag;

    private static int Eurocent(this decimal bedrag) => (int)(Math.Round(bedrag % 1, 2) * 100);

    #endregion

 

    #region Afgeleide eigenschappen

    private static bool HeeftEenhedenTot20(this int getal) => getal>0 && getal.Tientallen() < 2;

    private static bool HeeftTientallenVanaf20(this int getal) => getal.Tientallen() >= 2;

    private static bool HeeftHonderdtal(this int getal) => getal.Honderdtallen() == 1;

    private static bool HeeftHonderdtallen(this int getal) => getal.Honderdtallen() > 1;

    private static bool HeeftDuizendtal(this int getal) => getal.Duizendtallen() == 1;

    private static bool HeeftDuizendtallen(this int getal) => getal.Duizendtallen() > 1;

    private static bool HeeftMiljoentallen(this int getal) => getal.Miljoentallen() > 0;
    private static bool HeeftMiljardtallen(this int getal) => getal.Miljardtallen() > 0;

    private static bool HeeftCenten(this decimal bedrag) => bedrag.Eurocent() > 0;

    #endregion

 

    #region Samengestelde teksten

    private static string BedragTekst(this decimal bedrag) =>

      bedrag.HeeftCenten() ?

        $"{bedrag.Euro().Tekst()} euro en {bedrag.Eurocent().Tekst()} cent" :

        $"{bedrag.Euro().Tekst()} euro";

 

    private static string Tekst(this int getal) =>

      getal.HeeftMiljardtallen() ?

        $"{getal.Miljardtallen().TekstTotDuizend()} {Miljard} {getal.TekstTotMiljard()}".TrimEnd() :

        getal.TekstTotMiljard();

 

    private static string TekstTotMiljard(this int getal) =>

      getal.HeeftMiljoentallen() ?

        $"{getal.Miljoentallen().TekstTotDuizend()} {Miljoen} {getal.TekstTotMiljoen()}".TrimEnd() :

        getal.TekstTotMiljoen();

 

    private static string TekstTotMiljoen(this int getal) =>

      getal.HeeftDuizendtallen() ?

        $"{getal.Duizendtallen().TekstTotDuizend()}{Duizend} {getal.TekstTotDuizend()}".TrimEnd() :

        getal.TekstTotTweeDuizend();

 

    private static string TekstTotTweeDuizend(this int getal) =>

      getal.HeeftDuizendtal() ?

        $"{Duizend} {getal.TekstTotDuizend()}".TrimEnd() :

        getal.TekstTotDuizend();

 

    private static string TekstTotDuizend(this int getal) =>

      getal.HeeftHonderdtallen() ?

        $"{EenhedenTot20[getal.Honderdtallen()]}{Honderd}{getal.TekstTotHonderd()}" :

        getal.TekstTotTweeHonderd();

 

    private static string TekstTotTweeHonderd(this int getal) =>

      getal.HeeftHonderdtal() ?

        $"{Honderd}{getal.TekstTotHonderd()}" :

        getal.TekstTotHonderd();

 

    private static string TekstTotHonderd(this int getal) =>

      getal.HeeftTientallenVanaf20() ?

        $"{EenhedenVanaf20[getal.Eenheden()]}{TientallenVanaf20[getal.Tientallen() - 2]}" :

        getal.TekstTotTwintig();

 

    private static string TekstTotTwintig(this int getal) =>

      getal.HeeftEenhedenTot20() ?

        EenhedenTot20[getal % 20] :

        Nul;

    #endregion

 

    #region Public API

    public static string ToUitgeschrevenBedrag(this decimal bedrag)

    {

      if (bedrag < 0)

        throw new ArgumentOutOfRangeException($"Ongeldige invoer: {bedrag}, bedrag moet groter zijn dan 0");

 

      return bedrag.BedragTekst();

    }

 

    public static string ToUitgeschrevenTekst(this int getal)

    {

      if (getal < 0)

        throw new ArgumentOutOfRangeException($"Ongeldige invoer: {getal}, getal moet groter zijn dan 0");

 

      return getal.Tekst();

    }

    #endregion

  }

}

 

 

Output

Onderstaande is de output van een oplopende reeks gegenereerde bedragen waaraan enkele speciale gevallen zijn toegevoegd.

0,00
nul euro
1,02
een euro en twee cent
2,04
twee euro en vier cent
4,08
vier euro en acht cent
8,16
acht euro en zestien cent
16,32
zestien euro en tweeëndertig cent
32,64
tweeëndertig euro en vierenzestig cent
64,28
vierenzestig euro en achtentwintig cent
100,00
honderd euro
101,02
honderdeen euro en twee cent
128,56
honderdachtentwintig euro en zesenvijftig cent
256,12
tweehonderdzesenvijftig euro en twaalf cent
512,24
vijfhonderdtwaalf euro en vierentwintig cent
1000,00
duizend euro
1001,02
duizend een euro en twee cent
1024,48
duizend vierentwintig euro en achtenveertig cent
2048,96
tweeduizend achtenveertig euro en zesennegentig cent
4096,92
vierduizend zesennegentig euro en tweeënnegentig cent
8192,84
achtduizend honderdtweeënnegentig euro en vierentachtig cent
10000,00
tienduizend euro
10001,02
tienduizend een euro en twee cent
16384,68
zestienduizend driehonderdvierentachtig euro en achtenzestig cent
32768,36
tweeëndertigduizend zevenhonderdachtenzestig euro en zesendertig cent
65536,72
vijfenzestigduizend vijfhonderdzesendertig euro en tweeënzeventig cent
100000,00
honderdduizend euro
131072,44
honderdeenendertigduizend tweeënzeventig euro en vierenveertig cent
262144,88
tweehonderdtweeënzestigduizend honderdvierenveertig euro en achtentachtig cent
524288,76
vijfhonderdvierentwintigduizend tweehonderdachtentachtig euro en zesenzeventig cent
1000000,00
een miljoen euro
1000001,02
een miljoen een euro en twee cent
1000101,02
een miljoen honderdeen euro en twee cent
1001000,00
een miljoen duizend euro
1001001,02
een miljoen duizend een euro en twee cent
1002000,00
een miljoen tweeduizend euro
1048576,52
een miljoen achtenveertigduizend vijfhonderdzesenzeventig euro en tweeënvijftig cent
2097152,04
twee miljoen zevenennegentigduizend honderdtweeënvijftig euro en vier cent
4194304,08
vier miljoen honderdvierennegentigduizend driehonderdvier euro en acht cent
8388608,16
acht miljoen driehonderdachtentachtigduizend zeshonderdacht euro en zestien cent
10000000,00
tien miljoen euro
16777216,32
zestien miljoen zevenhonderdzevenenzeventigduizend tweehonderdzestien euro en tweeëndertig cent
33554432,64
drieëndertig miljoen vijfhonderdvierenvijftigduizend vierhonderdtweeëndertig euro en vierenzestig cent
67108864,28
zevenenzestig miljoen honderdachtduizend achthonderdvierenzestig euro en achtentwintig cent
101000001,02
honderdeen miljoen een euro en twee cent
101001001,02
honderdeen miljoen duizend een euro en twee cent
101001101,02
honderdeen miljoen duizend honderdeen euro en twee cent
134217728,56
honderdvierendertig miljoen tweehonderdzeventienduizend zevenhonderdachtentwintig euro en zesenvijftig cent
268435456,12
tweehonderdachtenzestig miljoen vierhonderdvijfendertigduizend vierhonderdzesenvijftig euro en twaalf cent
536870912,24
vijfhonderdzesendertig miljoen achthonderdzeventigduizend negenhonderdtwaalf euro en vierentwintig cent
2147483646,98
twee miljard honderdzevenenveertig miljoen vierhonderddrieëntachtigduizend zeshonderdzesenveertig euro en achtennegentig cent

 

Schrijf je in voor onze nieuwsbrief

Schrijf je in en ontvang vier keer per jaar per email het laatste nieuws over software ontwikkeling en DataLeaf.

Ik zorg er voor dat uw bedrijfsproces inzichtelijk wordt!