Cacophrene
Messages postés251Date d'inscriptionlundi 29 mars 2004StatutMembreDernière intervention 4 mars 2008
-
14 sept. 2006 à 08:46
Whismeril
Messages postés18991Date d'inscriptionmardi 11 mars 2003StatutContributeurDernière intervention27 mars 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.
Whismeril
Messages postés18991Date d'inscriptionmardi 11 mars 2003StatutContributeurDernière intervention27 mars 2024654 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és18991Date d'inscriptionmardi 11 mars 2003StatutContributeurDernière intervention27 mars 2024654 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
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és18991Date d'inscriptionmardi 11 mars 2003StatutContributeurDernière intervention27 mars 2024654 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.
mouradovik
Messages postés1Date d'inscriptionmercredi 10 octobre 2007StatutMembreDernière intervention11 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és23Date d'inscriptionmardi 2 novembre 2004StatutMembreDernière intervention 5 janvier 2007 18 sept. 2006 à 08:37
Salut,
Excellente source.je la trouve vraiment pratique.
jean_marc_n2
Messages postés170Date d'inscriptionjeudi 11 décembre 2003StatutMembreDernière intervention24 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és31Date d'inscriptionlundi 23 décembre 2002StatutMembreDernière intervention28 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és251Date d'inscriptionlundi 29 mars 2004StatutMembreDernière intervention 4 mars 20081 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és31Date d'inscriptionlundi 23 décembre 2002StatutMembreDernière intervention28 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 :
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és251Date d'inscriptionlundi 29 mars 2004StatutMembreDernière intervention 4 mars 20081 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és251Date d'inscriptionlundi 29 mars 2004StatutMembreDernière intervention 4 mars 20081 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.
7 mars 2017 à 21:00
Je l'ai postée là
http://codes-sources.commentcamarche.net/source/101858-ecrire-des-nombres-en-lettre-c-vb-net-et-vba
1 mars 2017 à 21:18
voici les corrections
VB.Net
C#
1 mars 2017 à 00:47
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
Le code 100% vb.net
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).
23 févr. 2017 à 21:38
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
A plusieurs reprises il y a des conversions en Int16, or .Net travaille en 32bits
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.
22 févr. 2017 à 09:50
11 oct. 2007 à 01:15
18 sept. 2006 à 08:37
Excellente source.je la trouve vraiment pratique.
16 sept. 2006 à 10:22
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
14 sept. 2006 à 20:12
Cordialement...
Béru
14 sept. 2006 à 18:14
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
14 sept. 2006 à 10:27
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.
14 sept. 2006 à 08:50
Allez cette fois c'est tout :-)
Cacophrène
14 sept. 2006 à 08:46
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