CONVERSION D'UN CHIFFRE EN LETTRE

Cacophrene Messages postés 251 Date d'inscription lundi 29 mars 2004 Statut Membre Dernière intervention 4 mars 2008 - 14 sept. 2006 à 08:46
Whismeril Messages postés 19024 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 18 avril 2024 - 7 mars 2017 à 21:00
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/39539-conversion-d-un-chiffre-en-lettre

Whismeril Messages postés 19024 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 18 avril 2024 656
7 mars 2017 à 21:00
Suite à une erreur relevée sur un script JS similaire, j'ai fait une correction.
Je l'ai postée là
http://codes-sources.commentcamarche.net/source/101858-ecrire-des-nombres-en-lettre-c-vb-net-et-vba
Whismeril Messages postés 19024 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 18 avril 2024 656
1 mars 2017 à 21:18
En transposant le code en VBA, j'ai trouvé 2 erreurs:
  • ça écrivait un mille au lieu de mille
  • ça testait si le nombre est trop grand en positif, mais pas en négatif

voici les corrections

VB.Net
Imports System
Imports System.Collections.Generic

Public Module NombreEnLettres
    Private jusqueSeize() As String = {"zéro", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf", "dix", "onze", "douze", "treize", "quatorze", "quinze", "seize"}

    Private dizaines() As String = {"rien", "dix", "vingt", "trente", "quarante", "cinquante", "soixante", "soixante", "quatre-vingt", "quatre-vingt"}

    Private resultat As List(Of String)

    ''' <summary>
    ''' Méthode d'extension de la classe double écrivant le nombre en lettres
    ''' </summary>
    ''' <param name="Nombre">Nombre à écrire</param>
    ''' <param name="LePays">Pays d'utilisation, pour spécificitées régionnales</param>
    ''' <param name="LaDevise">Devise à utliser</param>
    ''' <returns></returns>
    <System.Runtime.CompilerServices.Extension> _
    Public Function ToLettres(ByVal Nombre As Double, Optional ByVal LePays As Pays = Pays.France, Optional ByVal LaDevise As Devise = Devise.Aucune) As String

        resultat = New List(Of String)()

        Select Case Math.Sign(Nombre)
            Case -1
                resultat.Add("moins ")
                Nombre *= -1

            Case 0
                Return jusqueSeize(0)
        End Select

        If Nombre >= 1.0E+16 Then
            Return "Nombre trop grand"
        End If


        Dim partieEntiere As Int64 = CLng(Math.Floor(Nombre))
        Dim partieDecimale As Double = Nombre - partieEntiere

        Dim milliers() As String = {"", "mille", "million", "milliard", "billion", "billiard"}

        If partieEntiere > 0 Then
            Dim troisChiffres As New List(Of Integer)() 'liste qui scinde la partie entière en morceaux de 3 chiffres

            Do While partieEntiere > 0
                troisChiffres.Add(CInt(Math.Floor(partieEntiere Mod 1000)))
                partieEntiere \= 1000
            Loop

            Dim reste As Double = Nombre - partieEntiere

            For i As Integer = troisChiffres.Count - 1 To 0 Step -1
                Dim leNombre As Integer = troisChiffres(i)

                If leNombre > 1 Then 'valeurs de milliers au pluriel
                    resultat.Add(Ecrit3Chiffres(troisChiffres(i), LePays))
                    If i > 1 Then ' mille est invariable et "" ne prend pas de s
                        resultat.Add(milliers(i) & "s")
                    ElseIf i = 1 Then
                        resultat.Add(milliers(i))
                    End If
                ElseIf leNombre = 1 Then
                    If i <> 1 Then resultat.Add("un") 'on dit un million, mais pas un mille
                    resultat.Add(milliers(i))
                End If
                'on ne traite pas le 0, car on ne dit pas X millions zéro mille Y.
            Next i
        Else
            resultat.Add(jusqueSeize(0))
        End If

        Select Case LaDevise
            Case Devise.Dollar
                resultat.Add("$")

            Case Devise.Euro
                resultat.Add("€")

            Case Devise.FrancSuisse
                resultat.Add("CHF")

        End Select

        If LaDevise <> Devise.Aucune Then
            partieDecimale = Math.Round(partieDecimale, 2)
            If partieDecimale <> 0 Then
                resultat.Add("et")
                resultat.Add(Ecrire2Chiffres(CInt(Math.Floor(partieDecimale * 100)), LePays))
                resultat.Add("centimes")
            End If
        Else
            milliers = {"millième", "millionième", "milliardième"}

            'avec l'imprécision des nombres à virgules flotantes,  1234562.789 - 1234562 donne 0.78900000010617077 il faut donc compter le nombre de chiffres décimaux du nombre original et arrondir le resultat de la soustraction 
            Dim morceaux() As String = Nombre.ToString("G25").Split({"."c, ","c}) 'par défaut ToString arrondi à 10^-8, le format G25 oblige à écrire 25 caractères s'ils sont présents soit (au pire) 15 avant la virgule, la virgule et 9 après, split permet de découper le string obtenu

            If morceaux.Length = 2 Then 'il y a une partie décimale
                resultat.Add("et")

                Dim lenghtPartieDecimale As Integer = morceaux(1).Length
                If lenghtPartieDecimale > 9 Then
                    lenghtPartieDecimale = 9 'on se limite à 10^-9
                End If

                partieDecimale = Math.Round(partieDecimale, lenghtPartieDecimale)

                Dim i As Integer = 0
                Do While partieDecimale > 0
                    partieDecimale = partieDecimale * 1000
                    Dim valeur As Integer = CInt(Math.Floor(partieDecimale))
                    lenghtPartieDecimale -= 3
                    If lenghtPartieDecimale < 0 Then
                        lenghtPartieDecimale = 0
                    End If
                    partieDecimale = Math.Round(partieDecimale - valeur, lenghtPartieDecimale)

                    If valeur <> 0 Then
                        resultat.Add(Ecrit3Chiffres(valeur, LePays))
                        If valeur > 1 Then
                            resultat.Add(milliers(i) & "s")
                            i += 1
                        Else
                            resultat.Add(milliers(i))
                            i += 1
                        End If

                    End If
                Loop


            End If
        End If



        Return String.Join(" ", resultat)
    End Function

    ''' <summary>
    ''' Ecrit les nombres de 0 à 999
    ''' </summary>
    ''' <param name="Nombre">Nombre à écrire</param>
    ''' <param name="LePays">Pays d'utilisation, pour spécificitées régionnales</param>
    Private Function Ecrit3Chiffres(ByVal Nombre As Integer, ByVal LePays As Pays) As String
        If Nombre = 100 Then
            Return "cent"
        End If

        If Nombre < 100 Then
            Return Ecrire2Chiffres(Nombre, LePays)
        End If

        Dim centaine As Integer = Nombre \ 100
        Dim reste As Integer = Nombre Mod 100

        If reste = 0 Then 'Cent prend un s quand il est multiplié et non suivi d'un nombre, comme le cas de 100 est déjà traité on est face à un multiple
            Return jusqueSeize(centaine) & " cents"
        End If

        If centaine = 1 Then
            Return "cent " & Ecrire2Chiffres(reste, LePays) 'on ne dit pas un cent X, mais cent X
        End If

        Return jusqueSeize(centaine) & " cent " & Ecrire2Chiffres(reste, LePays)
    End Function

    ''' <summary>
    ''' Ecrit les nombres de 0 à 99
    ''' </summary>
    ''' <param name="Nombre">Nombre à écrire</param>
    ''' <param name="LePays">Pays d'utilisation, pour spécificitées régionnales</param>
    ''' <returns></returns>
    Private Function Ecrire2Chiffres(ByVal Nombre As Integer, ByVal LePays As Pays) As String
        If LePays <> Pays.France Then
            dizaines(7) = "septante"
            dizaines(9) = "nonante"
        End If
        If LePays = Pays.Suisse Then
            dizaines(8) = "huitante"
        End If

        If Nombre < 17 Then
            Return jusqueSeize(Nombre)
        End If

        Select Case Nombre 'cas particuliers de 71, 80 et 81
            Case 71 'en France 71 prend un et
                If LePays = Pays.France Then
                    Return "soixante et onze"
                End If

            Case 80 'en France et Belgique le vingt prend un s
                If LePays = Pays.Suisse Then
                    Return dizaines(8)
                Else
                    Return dizaines(8) & "s"
                End If

            Case 81 'en France et Belgique il n'y a pas de et
                If LePays <> Pays.Suisse Then
                    Return dizaines(8) & "-un"
                End If
        End Select


        Dim dizaine As Integer = Nombre \ 10
        Dim unite As Integer = Nombre Mod 10

        Dim laDizaine As String = dizaines(dizaine)

        If LePays = Pays.France AndAlso (dizaine = 7 OrElse dizaine = 9) Then
            dizaine -= 1
            unite += 10
        End If


        Select Case unite
            Case 0
                Return laDizaine

            Case 1
                Return laDizaine & " et un"

            Case 17, 18, 19 'pour 77 à 79 et 97 à 99
                unite = unite Mod 10
                Return laDizaine & "-dix-" & jusqueSeize(unite)

            Case Else
                Return laDizaine & "-" & jusqueSeize(unite)
        End Select
    End Function
End Module

Public Enum Pays
    France
    Belgique
    Suisse
End Enum

Public Enum Devise
    Aucune
    Euro
    FrancSuisse
    Dollar
End Enum



C#
using System;
using System.Collections.Generic;

namespace test
{
    public static class NombreEnLettres
    {
        private static string[] jusqueSeize = { "zéro", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf", "dix", "onze", "douze", "treize", "quatorze", "quinze", "seize" };

        private static string[] dizaines = { "rien", "dix", "vingt", "trente", "quarante", "cinquante", "soixante", "soixante", "quatre-vingt", "quatre-vingt"};

        private static List<string> resultat;

        /// <summary>
        /// Méthode d'extension de la classe double écrivant le nombre en lettres
        /// </summary>
        /// <param name="Nombre">Nombre à écrire</param>
        /// <param name="LePays">Pays d'utilisation, pour spécificitées régionnales</param>
        /// <param name="LaDevise">Devise à utliser</param>
        /// <returns></returns>
        public static string ToLettres(this double Nombre, Pays LePays = Pays.France, Devise LaDevise = Devise.Aucune)
        {

            resultat = new List<string>();

            switch (Math.Sign(Nombre))
            {
                case -1:
                    resultat.Add("moins ");
                    Nombre *= -1;
                    break;

                case 0:
                    return jusqueSeize[0];
            }

            if (Nombre >= 1e16)
                return "Nombre trop grand";

            
            Int64 partieEntiere = (Int64)(Nombre);
            double partieDecimale = Nombre - partieEntiere;

            string[] milliers = { "", "mille", "million", "milliard", "billion", "billiard" };

            if (partieEntiere > 0)
            {
                List<int> troisChiffres = new List<int>();//liste qui scinde la partie entière en morceaux de 3 chiffres

                while (partieEntiere > 0)
                {
                    troisChiffres.Add((int)(partieEntiere % 1000));
                    partieEntiere /= 1000;
                }

                double reste = Nombre - partieEntiere;



                for (int i = troisChiffres.Count - 1; i >= 0; i--)
                {
                    int nombre = troisChiffres[i];

                    if (nombre > 1)//valeurs de milliers au pluriel
                    {
                        resultat.Add(Ecrit3Chiffres(troisChiffres[i], LePays));
                        if (i > 1)// mille est invariable et "" ne prend pas de s 
                            resultat.Add(milliers[i] + "s");
                        else if (i == 1)
                            resultat.Add(milliers[i]);
                    }
                    else if (nombre == 1)
                    {
                        if (i != 1) resultat.Add("un");//on dit un million, mais pas un mille
                        resultat.Add(milliers[i]);
                    }
                    //on ne traite pas le 0, car on ne dit pas X millions zéro mille Y.
                }
            }
            else
                resultat.Add(jusqueSeize[0]);
            
            switch(LaDevise)
            {
                case Devise.Dollar:
                    resultat.Add("$");
                    break;

                case Devise.Euro:
                    resultat.Add("€");
                    break;

                case Devise.FrancSuisse:
                    resultat.Add("CHF");
                    break;

            }

            if (LaDevise != Devise.Aucune)
            {
                partieDecimale = Math.Round(partieDecimale, 2);
                if (partieDecimale != 0)
                {
                    resultat.Add("et");
                    resultat.Add(Ecrire2Chiffres((int)(partieDecimale * 100), LePays));
                    resultat.Add("centimes");
                }
            }
            else
            {
                milliers = new[] { "millième", "millionième", "milliardième" };

                //avec l'imprécision des nombres à virgules flotantes,  1234562.789 - 1234562 donne 0.78900000010617077 il faut donc compter le nombre de chiffres décimaux du nombre original et arrondir le resultat de la soustraction 
                string[] morceaux = Nombre.ToString("G25").Split(new []{'.',','});//par défaut ToString arrondi à 10^-8, le format G25 oblige à écrire 25 caractères s'ils sont présents soit (au pire) 15 avant la virgule, la virgule et 9 après, split permet de découper le string obtenu

                if (morceaux.Length == 2)//il y a une partie décimale
                {
                    resultat.Add("et");

                    int lenghtPartieDecimale = morceaux[1].Length;
                    if (lenghtPartieDecimale > 9)
                        lenghtPartieDecimale = 9;//on se limite à 10^-9

                    partieDecimale = Math.Round(partieDecimale, lenghtPartieDecimale);

                    int i = 0;
                    while (partieDecimale > 0)
                    {
                        partieDecimale = partieDecimale * 1000;
                        int valeur = (int)partieDecimale;
                        lenghtPartieDecimale -= 3;
                        if (lenghtPartieDecimale < 0)
                            lenghtPartieDecimale = 0;
                        partieDecimale = Math.Round(partieDecimale - valeur, lenghtPartieDecimale);

                        if (valeur != 0)
                        {
                            resultat.Add(Ecrit3Chiffres(valeur, LePays));
                            if (valeur > 1)
                                resultat.Add(milliers[i++] + "s");
                            else
                                resultat.Add(milliers[i++]);

                        }
                    }


                }
            }



            return string.Join(" ", resultat);
        }

        /// <summary>
        /// Ecrit les nombres de 0 à 999
        /// </summary>
        /// <param name="Nombre">Nombre à écrire</param>
        /// <param name="LePays">Pays d'utilisation, pour spécificitées régionnales</param>
        private static string Ecrit3Chiffres(int Nombre, Pays LePays)
        {
            if (Nombre == 100)
                return "cent";

            if(Nombre < 100)
                return Ecrire2Chiffres(Nombre, LePays);

            int centaine = Nombre / 100;
            int reste = Nombre % 100;

            if (reste == 0)//Cent prend un s quand il est multiplié et non suivi d'un nombre, comme le cas de 100 est déjà traité on est face à un multiple
                return jusqueSeize[centaine] + " cents";

            if (centaine == 1)
                return "cent " + Ecrire2Chiffres(reste, LePays);//on ne dit pas un cent X, mais cent X

            return jusqueSeize[centaine] + " cent " + Ecrire2Chiffres(reste, LePays);
        }

        /// <summary>
        /// Ecrit les nombres de 0 à 99
        /// </summary>
        /// <param name="Nombre">Nombre à écrire</param>
        /// <param name="LePays">Pays d'utilisation, pour spécificitées régionnales</param>
        /// <returns></returns>
        private static string Ecrire2Chiffres(int Nombre, Pays LePays)
        {
            if (LePays != Pays.France)
            {
                dizaines[7] = "septante";
                dizaines[9] = "nonante";
            }
            if (LePays == Pays.Suisse)
                dizaines[8] = "huitante";

            if (Nombre < 17)
                return jusqueSeize[Nombre];

            switch (Nombre)//cas particuliers de 71, 80 et 81
            {
                case 71://en France 71 prend un et
                    if (LePays == Pays.France)
                        return "soixante et onze";
                    break;

                case 80://en France et Belgique le vingt prend un s
                    if (LePays == Pays.Suisse)
                        return dizaines[8];
                    else
                        return dizaines[8] + "s";

                case 81://en France et Belgique il n'y a pas de et
                    if (LePays != Pays.Suisse)
                        return dizaines[8] + "-un";
                    break;
            }


            int dizaine = Nombre / 10;
            int unite = Nombre % 10;

            string laDizaine = dizaines[dizaine];

            if (LePays == Pays.France && (dizaine == 7 || dizaine == 9))
            {
                dizaine--;
                unite += 10;
            }


            switch (unite)
            {
                case 0:
                    return laDizaine;

                case 1:
                    return laDizaine + " et un";

                case 17://pour 77 à 79 et 97 à 99
                case 18:
                case 19:
                    unite = unite % 10;
                    return laDizaine + "-dix-" + jusqueSeize[unite];

                default:
                    return laDizaine + "-" + jusqueSeize[unite];
            }
        }
    }

    public enum Pays
    {
        France,
        Belgique,
        Suisse
    }

    public enum Devise
    {
        Aucune,
        Euro,
        FrancSuisse,
        Dollar
    }
}
Whismeril Messages postés 19024 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 18 avril 2024 656
1 mars 2017 à 00:47
Bonsoir

finalement, je me suis dit qu'une méthode d'extension est à la fois pratique et un bon exemple.
Il s'agit d'une méthode que l'on vient ajouter à une classe existante (ici double), pour le coup, ça s'écrit dans un module.

Quelques lignes de test, et ça permet de comprendre l'utilité des méthodes d'extension
        Dim lesNombres As New List(Of String)()
        Dim nombreExemple As Double = 123.456
        lesNombres.Add(nombreExemple.ToLettres()) 'on peut appliquer la méthode d'extension sur une variable de type double
        lesNombres.Add(3210987654321.2.ToLettres()) 'on peut aussi l'appliquer directement sur un nombre (Attention, il faut une virgule, sinon le nombre est pris comme un entier et la méthode n'étend pas les entiers)
        lesNombres.Add(123456789012345.12.ToLettres()) 'ce nombre n'existe pas en double, c'est 123456789012345.12 qui est transmis à la méthode
        lesNombres.Add(1234.123456789.ToLettres())
        lesNombres.Add((-4321.987654321).ToLettres())
        lesNombres.Add(1.0E+16.ToLettres()) 'ce nombre est trop grand
        lesNombres.Add(0.12345678961.ToLettres()) 'il y a trop de chiffres dérrière la virgule, le resultat sera arrondi
        lesNombres.Add(6795432.456.ToLettres(Pays.Belgique, Devise.Euro))



Le code 100% vb.net
Imports System
Imports System.Collections.Generic

Public Module NombreEnLettres
    Private jusqueSeize() As String = {"zéro", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf", "dix", "onze", "douze", "treize", "quatorze", "quinze", "seize"}

    Private dizaines() As String = {"rien", "dix", "vingt", "trente", "quarante", "cinquante", "soixante", "soixante", "quatre-vingt", "quatre-vingt"}

    Private resultat As List(Of String)

    ''' <summary>
    ''' Méthode d'extension de la classe double écrivant le nombre en lettres
    ''' </summary>
    ''' <param name="Nombre">Nombre à écrire</param>
    ''' <param name="LePays">Pays d'utilisation, pour spécificitées régionnales</param>
    ''' <param name="LaDevise">Devise à utliser</param>
    ''' <returns></returns>
    <System.Runtime.CompilerServices.Extension> _
    Public Function ToLettres(ByVal Nombre As Double, Optional ByVal LePays As Pays = Pays.France, Optional ByVal LaDevise As Devise = Devise.Aucune) As String

        If Nombre >= 1.0E+16 Then
            Return "Nombre trop grand"
        End If

        resultat = New List(Of String)()

        Select Case Math.Sign(Nombre)
            Case -1
                resultat.Add("moins ")
                Nombre *= -1

            Case 0
                Return jusqueSeize(0)
        End Select

        Dim partieEntiere As Int64 = CLng(Math.Floor(Nombre))
        Dim partieDecimale As Double = Nombre - partieEntiere

        Dim milliers() As String = {"", "mille", "million", "milliard", "billion", "billiard"}

        If partieEntiere > 0 Then
            Dim troisChiffres As New List(Of Integer)() 'liste qui scinde la partie entière en morceaux de 3 chiffres

            Do While partieEntiere > 0
                troisChiffres.Add(CInt(Math.Floor(partieEntiere Mod 1000)))
                partieEntiere \= 1000
            Loop

            Dim reste As Double = Nombre - partieEntiere



            For i As Integer = troisChiffres.Count - 1 To 0 Step -1
                'INSTANT VB NOTE: The variable nombre was renamed since Visual Basic will not allow local variables with the same name as parameters or other local variables:
                Dim nombre_Renamed As Integer = troisChiffres(i)

                If nombre_Renamed > 1 Then 'valeurs de milliers au pluriel
                    resultat.Add(Ecrit3Chiffres(troisChiffres(i), LePays))
                    If i > 1 Then ' mille est invariable et "" ne prend pas de s
                        resultat.Add(milliers(i) & "s")
                    ElseIf i = 1 Then
                        resultat.Add(milliers(i))
                    End If
                ElseIf nombre_Renamed = 1 Then
                    resultat.Add("un")
                    resultat.Add(milliers(i))
                End If
                'on ne traite pas le 0, car on ne dit pas X millions zéro mille Y.
            Next i
        Else
            resultat.Add(jusqueSeize(0))
        End If

        Select Case LaDevise
            Case Devise.Dollar
                resultat.Add("$")

            Case Devise.Euro
                resultat.Add("€")

            Case Devise.FrancSuisse
                resultat.Add("CHF")

        End Select

        If LaDevise <> Devise.Aucune Then
            partieDecimale = Math.Round(partieDecimale, 2)
            If partieDecimale <> 0 Then
                resultat.Add("et")
                resultat.Add(Ecrire2Chiffres(CInt(Math.Floor(partieDecimale * 100)), LePays))
                resultat.Add("centimes")
            End If
        Else
            milliers = {"millième", "millionième", "milliardième"}

            'avec l'imprécision des nombres à virgules flotantes,  1234562.789 - 1234562 donne 0.78900000010617077 il faut donc compter le nombre de chiffres décimaux du nombre original et arrondir le resultat de la soustraction 
            Dim morceaux() As String = Nombre.ToString("G25").Split({"."c, ","c}) 'par défaut ToString arrondi à 10^-8, le format G25 oblige à écrire 25 caractères s'ils sont présents soit (au pire) 15 avant la virgule, la virgule et 9 après, split permet de découper le string obtenu

            If morceaux.Length = 2 Then 'il y a une partie décimale
                resultat.Add("et")

                Dim lenghtPartieDecimale As Integer = morceaux(1).Length
                If lenghtPartieDecimale > 9 Then
                    lenghtPartieDecimale = 9 'on se limite à 10^-9
                End If

                partieDecimale = Math.Round(partieDecimale, lenghtPartieDecimale)

                Dim i As Integer = 0
                Do While partieDecimale > 0
                    partieDecimale = partieDecimale * 1000
                    Dim valeur As Integer = CInt(Math.Floor(partieDecimale))
                    lenghtPartieDecimale -= 3
                    If lenghtPartieDecimale < 0 Then
                        lenghtPartieDecimale = 0
                    End If
                    partieDecimale = Math.Round(partieDecimale - valeur, lenghtPartieDecimale)

                    If valeur <> 0 Then
                        resultat.Add(Ecrit3Chiffres(valeur, LePays))
                        If valeur > 1 Then
                            resultat.Add(milliers(i) & "s")
                            i += 1
                        Else
                            resultat.Add(milliers(i))
                            i += 1
                        End If

                    End If
                Loop


            End If
        End If



        Return String.Join(" ", resultat)
    End Function

    ''' <summary>
    ''' Ecrit les nombres de 0 à 999
    ''' </summary>
    ''' <param name="Nombre">Nombre à écrire</param>
    ''' <param name="LePays">Pays d'utilisation, pour spécificitées régionnales</param>
    Private Function Ecrit3Chiffres(ByVal Nombre As Integer, ByVal LePays As Pays) As String
        If Nombre = 100 Then
            Return "cent"
        End If

        If Nombre < 100 Then
            Return Ecrire2Chiffres(Nombre, LePays)
        End If

        Dim centaine As Integer = Nombre \ 100
        Dim reste As Integer = Nombre Mod 100

        If reste = 0 Then 'Cent prend un s quand il est multiplié et non suivi d'un nombre, comme le cas de 100 est déjà traité on est face à un multiple
            Return jusqueSeize(centaine) & " cents"
        End If

        If centaine = 1 Then
            Return "cent " & Ecrire2Chiffres(reste, LePays) 'on ne dit pas un cent X, mais cent X
        End If

        Return jusqueSeize(centaine) & " cent " & Ecrire2Chiffres(reste, LePays)
    End Function

    ''' <summary>
    ''' Ecrit les nombres de 0 à 99
    ''' </summary>
    ''' <param name="Nombre">Nombre à écrire</param>
    ''' <param name="LePays">Pays d'utilisation, pour spécificitées régionnales</param>
    ''' <returns></returns>
    Private Function Ecrire2Chiffres(ByVal Nombre As Integer, ByVal LePays As Pays) As String
        If LePays <> Pays.France Then
            dizaines(7) = "septante"
            dizaines(9) = "nonante"
        End If
        If LePays = Pays.Suisse Then
            dizaines(8) = "huitante"
        End If

        If Nombre < 17 Then
            Return jusqueSeize(Nombre)
        End If

        Select Case Nombre 'cas particuliers de 71, 80 et 81
            Case 71 'en France 71 prend un et
                If LePays = Pays.France Then
                    Return "soixante et onze"
                End If

            Case 80 'en France et Belgique le vingt prend un s
                If LePays = Pays.Suisse Then
                    Return dizaines(8)
                Else
                    Return dizaines(8) & "s"
                End If

            Case 81 'en France et Belgique il n'y a pas de et
                If LePays <> Pays.Suisse Then
                    Return dizaines(8) & "-un"
                End If
        End Select


        Dim dizaine As Integer = Nombre \ 10
        Dim unite As Integer = Nombre Mod 10

        Dim laDizaine As String = dizaines(dizaine)

        If LePays = Pays.France AndAlso (dizaine = 7 OrElse dizaine = 9) Then
            dizaine -= 1
            unite += 10
        End If


        Select Case unite
            Case 0
                Return laDizaine

            Case 1
                Return laDizaine & " et un"

            Case 17, 18, 19 'pour 77 à 79 et 97 à 99
                unite = unite Mod 10
                Return laDizaine & "-dix-" & jusqueSeize(unite)

            Case Else
                Return laDizaine & "-" & jusqueSeize(unite)
        End Select
    End Function
End Module

Public Enum Pays
    France
    Belgique
    Suisse
End Enum

Public Enum Devise
    Aucune
    Euro
    FrancSuisse
    Dollar
End Enum



Pour les règles d'orthographe je me suis basé sur ce site
http://leconjugueur.lefigaro.fr/frlesnombres.php

J'ai aussi appliqué des critères utilisés pour cette autre source
http://www.commentcamarche.net/faq/11100-vb6-net-vba-transformer-chiffres-en-lettre

Je donne aussi le code en C#. Les modules n'existant pas dans ce langage, les extensions s'écrivent dans l’équivalent d'une classe Shared => la classe static (qui n'a pas le même sens qu'en vb).
using System;
using System.Collections.Generic;

namespace test
{
    public static class NombreEnLettres
    {
        private static string[] jusqueSeize = { "zéro", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf", "dix", "onze", "douze", "treize", "quatorze", "quinze", "seize" };

        private static string[] dizaines = { "rien", "dix", "vingt", "trente", "quarante", "cinquante", "soixante", "soixante", "quatre-vingt", "quatre-vingt"};

        private static List<string> resultat;

        /// <summary>
        /// Méthode d'extension de la classe double écrivant le nombre en lettres
        /// </summary>
        /// <param name="Nombre">Nombre à écrire</param>
        /// <param name="LePays">Pays d'utilisation, pour spécificitées régionnales</param>
        /// <param name="LaDevise">Devise à utliser</param>
        /// <returns></returns>
        public static string ToLettres(this double Nombre, Pays LePays = Pays.France, Devise LaDevise = Devise.Aucune)
        {

            if (Nombre >= 1e16)
                return "Nombre trop grand";

            resultat = new List<string>();

            switch (Math.Sign(Nombre))
            {
                case -1:
                    resultat.Add("moins ");
                    Nombre *= -1;
                    break;

                case 0:
                    return jusqueSeize[0];
            }
            
            Int64 partieEntiere = (Int64)(Nombre);
            double partieDecimale = Nombre - partieEntiere;

            string[] milliers = { "", "mille", "million", "milliard", "billion", "billiard" };

            if (partieEntiere > 0)
            {
                List<int> troisChiffres = new List<int>();//liste qui scinde la partie entière en morceaux de 3 chiffres

                while (partieEntiere > 0)
                {
                    troisChiffres.Add((int)(partieEntiere % 1000));
                    partieEntiere /= 1000;
                }

                double reste = Nombre - partieEntiere;



                for (int i = troisChiffres.Count - 1; i >= 0; i--)
                {
                    int nombre = troisChiffres[i];

                    if (nombre > 1)//valeurs de milliers au pluriel
                    {
                        resultat.Add(Ecrit3Chiffres(troisChiffres[i], LePays));
                        if (i > 1)// mille est invariable et "" ne prend pas de s 
                            resultat.Add(milliers[i] + "s");
                        else if (i == 1)
                            resultat.Add(milliers[i]);
                    }
                    else if (nombre == 1)
                    {
                        resultat.Add("un");
                        resultat.Add(milliers[i]);
                    }
                    //on ne traite pas le 0, car on ne dit pas X millions zéro mille Y.
                }
            }
            else
                resultat.Add(jusqueSeize[0]);
            
            switch(LaDevise)
            {
                case Devise.Dollar:
                    resultat.Add("$");
                    break;

                case Devise.Euro:
                    resultat.Add("€");
                    break;

                case Devise.FrancSuisse:
                    resultat.Add("CHF");
                    break;

            }

            if (LaDevise != Devise.Aucune)
            {
                partieDecimale = Math.Round(partieDecimale, 2);
                if (partieDecimale != 0)
                {
                    resultat.Add("et");
                    resultat.Add(Ecrire2Chiffres((int)(partieDecimale * 100), LePays));
                    resultat.Add("centimes");
                }
            }
            else
            {
                milliers = new[] { "millième", "millionième", "milliardième" };

                //avec l'imprécision des nombres à virgules flotantes,  1234562.789 - 1234562 donne 0.78900000010617077 il faut donc compter le nombre de chiffres décimaux du nombre original et arrondir le resultat de la soustraction 
                string[] morceaux = Nombre.ToString("G25").Split(new []{'.',','});//par défaut ToString arrondi à 10^-8, le format G25 oblige à écrire 25 caractères s'ils sont présents soit (au pire) 15 avant la virgule, la virgule et 9 après, split permet de découper le string obtenu

                if (morceaux.Length == 2)//il y a une partie décimale
                {
                    resultat.Add("et");

                    int lenghtPartieDecimale = morceaux[1].Length;
                    if (lenghtPartieDecimale > 9)
                        lenghtPartieDecimale = 9;//on se limite à 10^-9

                    partieDecimale = Math.Round(partieDecimale, lenghtPartieDecimale);

                    int i = 0;
                    while (partieDecimale > 0)
                    {
                        partieDecimale = partieDecimale * 1000;
                        int valeur = (int)partieDecimale;
                        lenghtPartieDecimale -= 3;
                        if (lenghtPartieDecimale < 0)
                            lenghtPartieDecimale = 0;
                        partieDecimale = Math.Round(partieDecimale - valeur, lenghtPartieDecimale);

                        if (valeur != 0)
                        {
                            resultat.Add(Ecrit3Chiffres(valeur, LePays));
                            if (valeur > 1)
                                resultat.Add(milliers[i++] + "s");
                            else
                                resultat.Add(milliers[i++]);

                        }
                    }


                }
            }



            return string.Join(" ", resultat);
        }

        /// <summary>
        /// Ecrit les nombres de 0 à 999
        /// </summary>
        /// <param name="Nombre">Nombre à écrire</param>
        /// <param name="LePays">Pays d'utilisation, pour spécificitées régionnales</param>
        private static string Ecrit3Chiffres(int Nombre, Pays LePays)
        {
            if (Nombre == 100)
                return "cent";

            if(Nombre < 100)
                return Ecrire2Chiffres(Nombre, LePays);

            int centaine = Nombre / 100;
            int reste = Nombre % 100;

            if (reste == 0)//Cent prend un s quand il est multiplié et non suivi d'un nombre, comme le cas de 100 est déjà traité on est face à un multiple
                return jusqueSeize[centaine] + " cents";

            if (centaine == 1)
                return "cent " + Ecrire2Chiffres(reste, LePays);//on ne dit pas un cent X, mais cent X

            return jusqueSeize[centaine] + " cent " + Ecrire2Chiffres(reste, LePays);
        }

        /// <summary>
        /// Ecrit les nombres de 0 à 99
        /// </summary>
        /// <param name="Nombre">Nombre à écrire</param>
        /// <param name="LePays">Pays d'utilisation, pour spécificitées régionnales</param>
        /// <returns></returns>
        private static string Ecrire2Chiffres(int Nombre, Pays LePays)
        {
            if (LePays != Pays.France)
            {
                dizaines[7] = "septante";
                dizaines[9] = "nonante";
            }
            if (LePays == Pays.Suisse)
                dizaines[8] = "huitante";

            if (Nombre < 17)
                return jusqueSeize[Nombre];

            switch (Nombre)//cas particuliers de 71, 80 et 81
            {
                case 71://en France 71 prend un et
                    if (LePays == Pays.France)
                        return "soixante et onze";
                    break;

                case 80://en France et Belgique le vingt prend un s
                    if (LePays == Pays.Suisse)
                        return dizaines[8];
                    else
                        return dizaines[8] + "s";

                case 81://en France et Belgique il n'y a pas de et
                    if (LePays != Pays.Suisse)
                        return dizaines[8] + "-un";
                    break;
            }


            int dizaine = Nombre / 10;
            int unite = Nombre % 10;

            string laDizaine = dizaines[dizaine];

            if (LePays == Pays.France && (dizaine == 7 || dizaine == 9))
            {
                dizaine--;
                unite += 10;
            }


            switch (unite)
            {
                case 0:
                    return laDizaine;

                case 1:
                    return laDizaine + " et un";

                case 17://pour 77 à 79 et 97 à 99
                case 18:
                case 19:
                    unite = unite % 10;
                    return laDizaine + "-dix-" + jusqueSeize[unite];

                default:
                    return laDizaine + "-" + jusqueSeize[unite];
            }
        }
    }

    public enum Pays
    {
        France,
        Belgique,
        Suisse
    }

    public enum Devise
    {
        Aucune,
        Euro,
        FrancSuisse,
        Dollar
    }
}
Whismeril Messages postés 19024 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 18 avril 2024 656
23 févr. 2017 à 21:38
Bonsoir, la source date de 2006, à vue de nez elle doit être fonctionnelle (et à la lecture des commentaires), mais elle mélange Vb6 et VB.Net. Et même si souvent ça marche, parfois ça casse, donc autant éviter.
Le module et par extension les variables et méthodes globales vont à l'encontre du principe de la programmation objet, une classe Shared serait plus appropriée.
Call n'a plus aucune utilité en .Net
'Call Init() devient
Init()

A plusieurs reprises il y a des conversions en Int16, or .Net travaille en 32bits
chaine += " " & chiffre(Convert.ToInt16(Nombre))
donc pour aller lire la bonne valeur du tableau, après avoir converti nombre de double à int16, le complilateur va le reconvertir en int32. Ça fait perdre un peu de temps d'exécution.
Il y a d'autres points qui me chiffonnent, mais ne pouvant tester le code pour l'instant, je n'en dirais pas plus ce soir.
merciiiiiiiiiiiiiiiiiii
mouradovik Messages postés 1 Date d'inscription mercredi 10 octobre 2007 Statut Membre Dernière intervention 11 octobre 2007
11 oct. 2007 à 01:15
vous pouvez compiller votre source code sur convertir lettre en chiffre sur macro complementaire pour la utiliser direct sur Excel merci pour tout
lamarty Messages postés 23 Date d'inscription mardi 2 novembre 2004 Statut Membre Dernière intervention 5 janvier 2007
18 sept. 2006 à 08:37
Salut,
Excellente source.je la trouve vraiment pratique.
jean_marc_n2 Messages postés 170 Date d'inscription jeudi 11 décembre 2003 Statut Membre Dernière intervention 24 janvier 2009
16 sept. 2006 à 10:22
Hello,

j'ajouterais que contrairement à d'autres sources qui existent (sur ce site et ailleurs), ce programme ne gère pas les spécificités régonales:
en Belgique, 70 septante, 90 nonante, 76 = septante-six, etc. Idemn en Suisse, ou dans certains cantons on trouve 80=octante.

A compléter avec ces remarques pour avoir une chance de voir cette fonction utilisée un jour en dehors de l'hexagone :-)

Cordialement;

jean-marc
cs_Berurier Messages postés 31 Date d'inscription lundi 23 décembre 2002 Statut Membre Dernière intervention 28 juin 2013
14 sept. 2006 à 20:12
Sur le fond tu as raison. La mise en forme ne concerne que la partie finale, les deux instructions Replace qui évitent une succession de test durant la décomposition.

Cordialement...

Béru
Cacophrene Messages postés 251 Date d'inscription lundi 29 mars 2004 Statut Membre Dernière intervention 4 mars 2008 1
14 sept. 2006 à 18:14
Salut !

Les remarques d'orthographe ne relèvent pas de la mise en forme. Ton programme, dont le fonctionnement est très honorable, peut très bien gérer les traits d'union et les "s" dans des cas particuliers. Tu n'as besoin que de quelques If...Then en plus (ceux-ci ont d'ailleurs tout à fait leur place !). Puisque l'idée est bonne, autant la soigner jusqu'au bout non (plutôt que de faire un calque en lettre de la décomposition) ?

Cordialement,
Cacophrène
cs_Berurier Messages postés 31 Date d'inscription lundi 23 décembre 2002 Statut Membre Dernière intervention 28 juin 2013
14 sept. 2006 à 10:27
Zut, j'ai raté mon copier-coller.

J'espère que ce sera plus clair

En effet "initié" est sans doute un peu fort.
En fait j'ai déposé ce code pour sa méthode.
Je reprend un exemple :

212346523

Je vais le décomposer comme suit

1) (212 * 10^6) + (346 * 10^3) + (5*100) + ((2*10) + 3)
2) ((2 *100) + 12) * 10^6 + ((3*100) + 46 ) * 10^3 + (5*100) + (2*10) +3
3) ((2 *100) + 12 ) * 10^6 + ((3*100) + ((4 *10) + 6 ) ) * 10^3 + (5*100) + (2*10) +3
4) deux cent douze millions trois cent quarante six mille cinq cent vingt trois

Vous noterez que vous pouvez lire le chiffre.

Au premier passage dans la fonction décompose
je teste le chiffre par rapport au valeurs milliard,millier,centaine,etc...

Prenons le cas ci-dessus. 212346523

Par récursivité, les appels se feront de cette manière

Decompose(212346523) => < 1 milliard et > 1 million , je divise par 1 million
Decompose(212) => chaine = deux cent
Decompose(12) ==> chaine = deux cent douze

J'ajoute «million(s) » et je reprend le reste

Decompose(346523) < 1000000 et > 1000 , je divise par mille
Decompose(346) => chaine = deux cent douze millions trois cent
Decompose(46) => chaine = deux cent douze millions trois cent quarante six

J'ajoute «mille » et je reprend le reste

Decompose(523) chaine = deux cent douze millions trois cent mille cinq cent
Decompose(23) => chaine = deux cent douze trois cent mille cinq cent vingt-trois



Ensuite, il reste la mise forme...

J'ai utilisé la structure if..then parce qu'il y des cas particuliers pour tester la grandeur du chiffre à décomposer et parce que je n'en vois pas d'autre. Cependant je suis preneur de toute autre solution.
Cacophrene Messages postés 251 Date d'inscription lundi 29 mars 2004 Statut Membre Dernière intervention 4 mars 2008 1
14 sept. 2006 à 08:50
Si, j'oubliais quelque chose : le niveau, "initié". C'est peut-être un peu fort. Récursivité simple, pas d'API, sujet "concret", structures élémentaires (If...Then...Else), opérations simples (concaténation, division, etc...). Je ne sais pas, c'est à voir, mais ça me semble un petit peu fort.

Allez cette fois c'est tout :-)

Cacophrène
Cacophrene Messages postés 251 Date d'inscription lundi 29 mars 2004 Statut Membre Dernière intervention 4 mars 2008 1
14 sept. 2006 à 08:46
Salut !

Etant donné que ce code .NET est très proche d'un code VB6 classique, je me suis penché dessus (mais je ne pratique pas VB.NET). Après quelques tests, il reste encore qelques de points incorrects (c'est pour ainsi dire toujours les mêmes problèmes, et pourtant il y a déjà de telles sources sur ce site). Plus précisément :

1. Les traits d'union manquent dans certains cas (par exemple 43 est orthographié "quarante trois" et non pas "quarante-trois")

2. "Cent" ne prend de s au pluriel que lorsqu'il n'est pas suivi d'autre chose. Ainsi 200 "deux cents" mais 201 "deux cent un".

3. Il arrive qu'il y ait deux espaces contigus. Par exemple si je demande d'écrire 30001, j'obtiens "trente__mille_un" au lieu de "trente_mille_un" (pour des raisons de visibilité, ici un underscore = un espace)/

Voilà quelques petites choses à corriger. Côté programmation, ça manque peut-être un peu de commentaires.

Voilà voilà ! Je n'ai pas noté.

Cordialement,
Cacophrène
Rejoignez-nous