IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Ecrire un nombre en toutes lettres


Sujet :

Python

  1. #1
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 751
    Points
    1 751
    Par défaut Ecrire un nombre en toutes lettres
    Bonjour,
    j'ai fait le code suivant et je voudrais le soumettre à vos critiques constructives.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    #!/usr/bin/env python
     
    # This code only works with Python 3.
     
    # The following dictionnary will be used to name
    # a number by finding its pieces of three digits.
    TEN_POWERS = {
        3: "mille",
        6: "million",
        9: "milliard"
                 }
     
    SPECIAL_NUMBERS_NAMES = {
        '0': "zéro",
        '1': "un",
        '2': "deux",
        '3': "trois",
        '4': "quatre",
        '5': "cinq",
        '6': "six",
        '7': "sept",
        '8': "huit",
        '9': "neuf",
     
        '10': "dix",
        '11': "onze",
        '12': "douze",
        '13': "treize",
        '14': "quatorze",
        '15': "quinze",
        '16': "seize",
     
        '20': "vingt",
        '30': "trente",
        '40': "quarante",
        '50': "cinquante",
        '60': "soixante",
        '70': "soixante-dix",
        '80': "quatre-vingt",
        '90': "quatre-vingt-dix",
     
        '100': "cent"
                            }
     
    # We add the power of ten to SPECIAL_NUMBERS_NAMES.
    # We also find the biggest number of digits allowed.
    BIGGEST_NUMBER_DIGITS = 0
    for onePower in TEN_POWERS:
        SPECIAL_NUMBERS_NAMES['1' + '0'*onePower] = TEN_POWERS[onePower]
     
        if BIGGEST_NUMBER_DIGITS < onePower:
            BIGGEST_NUMBER_DIGITS = onePower
     
    BIGGEST_NUMBER_DIGITS += 3
     
    def printer(number, checkValidity = True):
        if checkValidity:
            try:
                number = int(number)
                kindOfNumber = 'integer'
    # We work with a string.
                number = str(number)
                checkValidity = False
            except:
                raise ValueError('number = "' + str(number) + '" must be an integer')
        else:
    # We clean unusefull zeros...
            number = str(int(number))
     
    # We have a base number.
        if number in SPECIAL_NUMBERS_NAMES:
            return SPECIAL_NUMBERS_NAMES[number]
     
    # We have a number less equal to 99.
    #
    # 0, 1, ... , 9 have been already treated.
    # 10, 11, 12, 13, 14, 15, 16 have been already treated.
    # 20, ... , 80 and 90 have been already treated.
        if len(number) == 2:
    # We have to take care of things like 76 and 94.
            if number[0] in ['7', '9']:
                unityName = printer( number = '1' + number[1],
                                     checkValidity = False )
                special = (int(number[0]) - 1)* 10
                decimalName = printer( number = special,
                                       checkValidity = False )
            else:
                unityName = printer(number[1])
                decimalName = printer( number = number[0] + '0',
                                       checkValidity = False )
     
            if number[1] == '1' and number[0] not in ['8', '9']:
                answer = '{0} et {1}'
            else:
                answer = '{0}-{1}'
     
            return answer.format(decimalName, unityName)
     
    # We have a number between 101 and 999.
    # 100 has been already treated.
        if len(number) == 3:
            centName = printer( number = '100',
                                checkValidity = False )
     
            if number[0] != '1':
                centName = printer( number = number[0],
                                    checkValidity = False ) \
                           + ' ' + centName
     
            decimalPartName = printer( number = number[1:],
                                       checkValidity = False )
     
            if decimalPartName == SPECIAL_NUMBERS_NAMES['0']:
    # Gramatical french boring rules...
                if number[0] != '1':
                    centName += 's'
                return centName
     
            return centName + ' ' + decimalPartName
     
    # We have to split the number in pieces of three digits.
        if len(number) < BIGGEST_NUMBER_DIGITS:
            piecesOfDigits = []
            piecesOfNames = []
     
            while number:
                actualPieceOfDigits = number[-3:]
                piecesOfDigits.append( printer( number = actualPieceOfDigits,
                                                checkValidity = False ) )
                number = number[:-3]
     
            answer = ''
     
            for powerOfTen in range(len(piecesOfDigits)-1, -1, -1):
                actualPieceOfDigits = piecesOfDigits[powerOfTen]
     
                if actualPieceOfDigits != SPECIAL_NUMBERS_NAMES['0']:
                    if powerOfTen:
                        powerName = TEN_POWERS[powerOfTen*3]
     
                        if actualPieceOfDigits == SPECIAL_NUMBERS_NAMES['1']:
                            piecesOfNames.append( powerName )
     
                        else:
    # Gramatical french boring rules...
                            if powerName != SPECIAL_NUMBERS_NAMES['1000']:
                                powerName += 's'
     
     
                            if actualPieceOfDigits[-len('cents'):] == 'cents':
                                actualPieceOfDigits = actualPieceOfDigits[:-1]
     
                            piecesOfNames.append( actualPieceOfDigits + ' ' + powerName )
     
                    else:
                        piecesOfNames.append( actualPieceOfDigits )
     
            return ' '.join(piecesOfNames)
     
     
        raise ValueError('number = "' + str(number) + '" is too big.')
     
     
    ###############
    ###############
    ##           ##
    ## FOR TESTS ##
    ##           ##
    ###############
    ###############
     
    if __name__ == '__main__':
        import random
     
        randomTest = True
        #randomTest = False
        nMin = 0
        nMax = 10**5
        nbOfTest = 20
     
        test = [
            4,
            400,
            18400,
            400567,
            91,
            120000567,
            12345678081,
            '1000' + '0'*9,
               ]
     
        if randomTest:
            nMax += 1
            for i in range(nbOfTest):
                oneNumber = random.randint(nMin, nMax)
                print(str(oneNumber))
                print('\t' + printer(oneNumber))
     
        else:
            for oneNumber in test:
                print(str(oneNumber))
                print('\t' + printer(oneNumber))

  2. #2
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Avril 2004
    Messages
    1 059
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 1 059
    Points : 1 396
    Points
    1 396
    Par défaut
    AH !!
    je le prends comme un defi ...

    j'arrive.

  3. #3
    Nouveau membre du Club Avatar de arnaudk
    Inscrit en
    Février 2009
    Messages
    38
    Détails du profil
    Informations forums :
    Inscription : Février 2009
    Messages : 38
    Points : 36
    Points
    36
    Par défaut
    Pas le temps de regarder, mais on a fait un exo à ce sujet dans pyromaths.
    Au pire regarde dans les sources si tu veux comparer.

  4. #4
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 751
    Points
    1 751
    Par défaut
    Ok là je bosse sur une version avec des TRES grands nombres, et aussi avec la possibilité d'écrire un autre de grandeur.

    Ensuite je ferais la traduction inverse MOT ---> CHIFFRES.

    Citation Envoyé par arnaudk Voir le message
    Pas le temps de regarder, mais on a fait un exo à ce sujet dans pyromaths.
    Au pire regarde dans les sources si tu veux comparer.
    Merci mais où dois-je regarder dans les sources ?

  5. #5
    Nouveau membre du Club Avatar de arnaudk
    Inscrit en
    Février 2009
    Messages
    38
    Détails du profil
    Informations forums :
    Inscription : Février 2009
    Messages : 38
    Points : 36
    Points
    36
    Par défaut
    Dans le dossier sixiemes, fichier decimaux.py.

    Sinon, je me suis inspiré de ce script http://www.miakinen.net/vrac/nombres pour faire un petit exo en php pour mes 6èmes, histoire de s'entrainer "on the web".

  6. #6
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 751
    Points
    1 751
    Par défaut
    Ok je vais y jeter un oeil.

  7. #7
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 751
    Points
    1 751
    Par défaut
    Finalement j'ai fini une version ENTIER===>NOM, et j'ai aussi fait NOM===>ENTIER avec la possibilité de faire des petites fautes de frappe.

  8. #8
    Membre régulier
    Homme Profil pro
    Etudiant CNAM (DIE20)
    Inscrit en
    Janvier 2010
    Messages
    151
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant CNAM (DIE20)

    Informations forums :
    Inscription : Janvier 2010
    Messages : 151
    Points : 97
    Points
    97
    Par défaut
    Bonsoir rambc,

    Ce que tu as fait est a priori exactement ce que je recherche
    Si tu es d'accord pour mettre ton code en licence Creative Commons BY-NC-SA ou compatible, cela m'intéresse énormément. Je pourrai en effet l'intégrer à mon projet de logiciel d'entrainement au calcul que je développe en ce moment (pour mon niveau 4 de difficulté, voir ici)

  9. #9
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 751
    Points
    1 751
    Par défaut
    Il faudrait voir dans quel cadre va fonctionner ton logiciel. De plus, la version ci-dessus à quelque gentil bug que j'ai corrigé sur une autre version qui supporte trois types d'écriture.

    A priori, je serais plus pour une licence de type GPL.

  10. #10
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 751
    Points
    1 751
    Par défaut
    Citation Envoyé par spirzouf Voir le message
    Ce n'est pas une licence pour les oeuvres artistiques.

  11. #11
    Membre régulier
    Homme Profil pro
    Etudiant CNAM (DIE20)
    Inscrit en
    Janvier 2010
    Messages
    151
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant CNAM (DIE20)

    Informations forums :
    Inscription : Janvier 2010
    Messages : 151
    Points : 97
    Points
    97
    Par défaut
    Citation Envoyé par rambc Voir le message
    Ce n'est pas une licence pour les œuvres artistiques.
    Bonjour et merci de m'avoir répondu,

    Je suis loin d'être expert en licences, j'avais juste voulu poser quelques 'limites' à mon travail avant de le poster.
    Cela dit, le code d'un programme est une œuvre de l'esprit et je ne crois pas qu'il y ait d'obstacle à le placer sous CC même si effectivement, cela n'a pas l'air d'être encouragé / recommandé.
    Je ne connais rien aux licences GPL mais je crois qu'il n'y a pas d'obstacle à l'utilisation commerciale ? Ce qui je l'avoue me gêne beaucoup (c'est un point de vue strictement personnel bien sûr) !
    En fait la seule limite importante que j'ai mise avec cette licence CC est donc l'impossibilité (sans accord du/des auteurs) d'une utilisation commerciale... sinon, le programme peut être modifié et distribué sous la même licence que le programme initial, en listant le / les auteur(s).

    S'il y a des experts en licences, je serais intéressé par leurs lumières, mais je crois qu'on entre dans le débat licence libre vs licence ouverte
    Cela dit, j'imagine qu'un programme en licence ouverte comme le miens peut faire appel à programme GPL comme le tiens s'il l'utilise en tant que module séparé, non (EDIT : quoique, ça n'a pas l'air évident en fait...) ?

  12. #12
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Points : 1 658
    Points
    1 658
    Par défaut
    Je me suis intéressé au code et au problème de rambc depuis qu’il a été posté.
    De critique constructive en critique corrective, j’en suis arrivé à en faire une refonte. Ou plutôt une fonte.


    ---------------------------------


    Contrairement à ce que tu dis, rambc, je n’ai pas constaté de bug ( je parle des bugs qui font planter un programme).
    De quels gentils bugs parles tu, stp ?

    Il faut juste corriger
    if len(number) < BIGGEST_NUMBER_DIGITS:
    en
    if len(number) <= BIGGEST_NUMBER_DIGITS:

    La règle orthographique des 80 n’est aussi pas respectée. On doit écrire:
    80 quatre-vingts
    180 cent qatre-vingts
    480 quatre cent quatre-vingts
    80380 quatre-vingt mille quatre-vingt
    80780180 quatre-vingts millions sept cent quatre-vingt mille cent quatre-vingts
    et ton code ne met de ’s’ à aucun ’quatre-vingt’

    À part ça, tous les autres résultats sont identiques à ceux que j’obtiens avec mon code,
    les règles sur ’cent’, et sur ’mille’ invariable, sont respectées,
    et y a pas de bug.



    ---------------------------------



    Cependant l’écriture donne une impression de rédaction pressée car il y a des choses balluches.

    - À quoi sert kindOfNumber définie dans l’instruction kindOfNumber = 'integer' ?

    - L’initialisation préalable BIGGEST_NUMBER_DIGITS = 0
    suivie de son incrémentation dans
    for onePower in TEN_POWERS:
    ....SPECIAL_NUMBERS_NAMES['1' + '0'*onePower] = TEN_POWERS[onePower]
    ....if BIGGEST_NUMBER_DIGITS < onePower:
    ........BIGGEST_NUMBER_DIGITS = onePower

    et de sa finalisation
    BIGGEST_NUMBER_DIGITS += 3
    peuvent avantageusement être remplacées par
    BIGGEST_NUMBER_DIGITS = max(TEN_POWERS) + 3.
    Eh oui, il n’est même pas nécessaire d’écrire max(TEN_POWERS.keys())

    - centName = printer( number = '100',
    checkValidity = False )

    est assez rigolo car il suffit d’écrire centName = ’cent’

    - actualPieceOfDigits = number[-3:]
    piecesOfDigits.append( printer( number = actualPieceOfDigits,checkValidity = False ) )

    Pourquoi ne pas écrire plus simplement
    piecesOfDigits.append( printer( number[-3:]
    ,False ) )
    ?

    Il y a sans doute d’autres chose mais c’est plus ou moins des détails.



    ----------------------------------------------




    Ce qui l’est moins, c’est l’algorithme, qui me paraît traînant par absence de vouloir toucher les objectifs en portant le fleuret au plus direct.

    Ton code me donne l’impression que tu as cherché à garder pour le dictionnaire SPECIAL_NUMBERS_NAMES une taille la plus petite possible.
    Ce choix conduit à un bourgeonnement (le coup du format(decimalName, unityName) qui nécessite de créer préalablement des objets decimalName et unityName) et une ramification du code (par obligation d’envisager des cas divers).

    En outre, je trouve compliqué le traitement des règles orthographiques:
    if actualPieceOfDigits[-len('cents'):] == 'cents':
    actualPieceOfDigits = actualPieceOfDigits[:-1] ).

    pffffffffffffff
    J’avoue que je me suis cassé la tête sur ces règles avant de trouver une voie de simplification, mais quand même.....

    Je confesse donc que je n’ai pas encore une pleine compréhension de ce qui se passe dans la section qui suit
    if len(number) <= BIGGEST_NUMBER_DIGITS: dans ton code.



    ------------------------------------




    J’ai préféré m’amuser à voir si je pouvais arriver à épurer tout ça.

    Je suis parti de ton canevas général, rambc, ne trouvant rien à y redire:
    - si number est dans SPECIAL_NUMBERS_NAMES, on renvoie son expression en mots SPECIAL_NUMBERS_NAMES[number]
    - sinon, s’il est de longueur 2, on le traite tout de suite
    - sinon, s’il est de longueur 3, on le traite tout de suite aussi, en traitant number[1:] par appel récursif de printer()
    - sinon, number étant de longueur >= 4, on le découpe en morceaux de 3 digits sur lesquels on appelle récursivement printer()
    OK.


    Enfin...presque...

    J’ai tout de suite estimé que les appels récursifs pour les nombres de 2 digits, ça a quelque chose du marteau-piqueur sur une noisette.
    Pour traiter par exemple 76,
    devoir définir special = (int(’7’) - 1)* 10 = (7-1)*10 = 60 pour pouvoir appeler printer() sur le nombre 60,
    c’est bien compliqué.
    Alors qu’on pourrait faire en sorte d’appeler directement SPECIAL_NUMBERS_NAMES[’60’] , et d’une.
    Et de deux, qu’on pourrait se demander comment obtenir directement ’soixante’ à partir de ’7’ sans avoir à faire ces calculs pour tomber sur 60.

    La solution qui me plaît à moi, c’est de rajouter dans SPECIAL_NUMBERS_NAMES, ou un autre dictionnaire , des données qui vont simplifier la vie.

    Parce que je pars du principe que les dictionnaires sont des conteneurs ultra réactifs lors de la recherche d’une valeur et qu’il est donc plus avantageux d’ajouter quelques items (clé,valeur) dans un dictionnaire que de trafiquer avec des calculs, des objets référencés excédentaires et des instructions if encombrant le code.

    C’est à mon avis plus avantageux aussi bien au niveau de la lisibilité du code que de sa rapidité d’exécution.

    Cependant il faut garder un équilibre entre ce qu’on gagne en ajoutant des items dans un dictionnaire et ce qu’on perd en alourdissant le dit dictionnaire.
    Je suis donc resté modéré dans la mise en œuvre de ce principe, cherchant à obtenir une simplification importante du code avec peu d’inscriptions de nouveaux items dans des dictionnaires.

    C’est ainsi que par rapport à ton code, rambc, je me suis limité à

    - ajouter dans SPECIAL_NUMBERS_NAMES les nombres ’01’,’02’,’03’,'04','05','06','07','08','09'parce que cela permet d’éviter bien des complications, notamment des appels récursifs de la fonction printer() sur ces nombres: à la place il suffit de solliciter le dictionnaire SPECIAL_NUMBERS_NAMES

    - renommer SPECIAL_NUMBERS_NAMES en ADJCTV_NUMBERS car c’est un nom qui correspond mieux à la nature des nombres jusqu’à 1000000.
    Les appellations MILLION, MILLIARD, BILLION etc sont des noms numéraux, avec des pluriels avec ’s’.
    Tandis que les un,deux,trois,...dix,onze,douze.....vingt, trente, quarante...quatre-vingts, cent, mille et leurs combinaisons sont des adjectifs numéraux , avec des règles particulières d’orthographe de leurs pluriels.

    - ajouter aussi les nombres ’21’,’31’,’41’,’51’,’61’,’71’ dont l’expression avec des mots comporte ’et’. Cela permet à vaiment très peu de frais d’éviter toute une combinatoire complexe jonglant avec des tirets et des ’et’ juste pour 6 nombres.

    - dans la foulée, ajouter aussi ’17’,’18’,’19’ qui évite la combinatoire superflue de ’dix’ avec ’sept’, ’huit’ et ’neuf’ à zéro intérêt.




    Autre remarque d’ordre algorithmique:
    je ne vois pas la motivation à encombrer le code avec des checkValidity = False dans tous les appels de fonctions récursifs et de n’avoir la définition implicite de ce paramètre que dans le premier appel de printer() sur un nombre.
    J’ai donc fait l’inverse. Si c’est une mauvaise idée, merci de m’indiquer pourquoi.




    ---------------------------------



    Premièrement , j’aboutis à un code plus lisible et plus immédiatement compréhensible.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    # -*- coding: cp1252 -*-
    from time import clock
     
    t1 = clock()
    for xx in xrange(100):
     
        ADJCTV_NUMBERS = {  '0': "zéro"     ,
                            '1': "un"       ,   '2': "deux"     ,   '3': "trois"    ,
                           '01': "un"       ,  '02': "deux"     ,  '03': "trois"    ,
                            '4': "quatre"   ,   '5': "cinq"     ,   '6': "six"      ,
                           '04': "quatre"   ,  '05': "cinq"     ,  '06': "six"      ,
                            '7': "sept"     ,   '8': "huit"     ,   '9': "neuf"     ,
                           '07': "sept"     ,  '08': "huit"     ,  '09': "neuf"     ,
                           '10': "dix"      ,
                           '11': "onze"     ,  '12': "douze"    ,  '13': "treize"   ,
                           '14': "quatorze" ,  '15': "quinze"   ,  '16': "seize"    ,
                           '17': "dix-sept" ,  '18': "dix-huit" ,  '19': "dix-neuf" ,
     
                           '20': "vingt"             ,   '21': 'vingt et un'        ,
                           '30': "trente"            ,   '31': 'trente et un'       ,
                           '40': "quarante"          ,   '41': 'quarante et un'     ,
                           '50': "cinquante"         ,   '51': 'cinquante et un'    ,
                           '60': "soixante"          ,   '61': 'soixante et un'     ,
                           '70': "soixante-dix"      ,   '71': 'soixante et onze'   ,
                           '80': "quatre-vingts"     ,
                           '90': "quatre-vingt-dix"  ,
     
                           '100': "cent"        , '200': "deux cents", '300': "trois cents",
                           '400': "quatre cents", '500': "cinq cents", '600': "six cents"  ,
                           '700': "sept cents"  , '800': "huit cents", '900': "neuf cents" }
     
     
        # We will use special prefix for numbers 'x2' to 'x9' with x = 2,3,4,5,6,7
        # and for numbers '81' to '89' and '91' to '99'.
        TEN_PREFIXES = {                    '2': "vingt-"        , '3': "trente-"       ,
                         '4': "quarante-" , '5': "cinquante-"    , '6': "soixante-"      ,
                         '7': "soixante-" , '8': "quatre-vingt-" , '9': "quatre-vingt-"  }
     
     
        # The following dictionary will be used to name
        # a number by finding its pieces of three digits.
        # Système de noms utilisé: échelle longue.
        TEN_POWERS = {   6: 'MILLION'     ,  9: 'MILLIARD'     ,
                        12: 'BILLION'     , 15: 'BILLIARD'     ,
                        18: 'TRILLION'    , 21: 'TRILLIARD'    ,
                        24: 'QUADRILLION' , 27: 'QUADRILLIARD' ,
                        30: 'QUINTILLION' , 33: 'QUINTILLIARD' ,
                        36: 'SEXTILLION'  , 39: 'SEXTILLIARD'   }
        aa = '1000'+len(TEN_POWERS)*'000'
        TEN_POWERS_NUMBERS = tuple( aa[0:1+p] for p in TEN_POWERS )
     
     
        # We find the biggest number of digits allowed.
        BIGGEST_DIGITS_NUMBER = max(TEN_POWERS) + 3
     
     
    t2 = clock()
     
     
     
     
    def printer(number, checkValidity = False):
     
        if checkValidity:
            try:
                number = str(int(number)) # We work with a string and we clean unusefull zeros...
            except:
                raise ValueError('number = "' + str(number) + '" must be an integer')
     
     
     
        # If we have a base adjective number.
        if number in ADJCTV_NUMBERS:
            return ADJCTV_NUMBERS[number]
        # Now, the number is necessarily of length > 1 because all the 1-digit numbers
        # are in the ADJCTV_NUMBERS dictionnary.
     
     
     
        # If we have a 2 digits number, first digit can't be '0', even with checkValidity==False
        # because numbers '01','02','03','04','05','06','07','08','09' have been already treated.
        # The numbers '11','12','13','14','15','16','17','18','19','20','21' and '30','31','40',
        # '41','50','51','60','61','70','71' and '80','90' have also been already treated.
        # We have to take care of things like 76 and 94.
        elif len(number) == 2:
            if number[0] in '79':
                return TEN_PREFIXES[number[0]] + ADJCTV_NUMBERS['1' + number[1]]
            else: # that is to say number[0] in ('2','3','4','5','6','8')
                return TEN_PREFIXES[number[0]] + ADJCTV_NUMBERS[number[1]]                          
     
     
     
        # We have a number between '101' and '999'.
        # Hundreds '100','200','300','400','500','600','700','800','900' have been already treated.
        # So number[1:] can't be '00' and printer(number[1:]) always exists.
        # Numbers of length 3 can't be of type '0xx': if checkValidity==True, the heading '0' has
        # been removed by str(int(number)); if number have been called upon with printer(number)
        # with checkValidity==False by default, the number have been previously treated with
        # lstrip('0')(see section len(number) <= BIGGEST_DIGITS_NUMBER)
        elif len(number) == 3:
            if number[0]!='1': # for numbers '201' to '299', '301' to '399', ...., '901' to '999'.
                return  ADJCTV_NUMBERS[number[0]] + ' cent ' + printer(number[1:])
            else: # for numbers '101' to '199'
                return  'cent ' + printer(number[1:])
     
     
     
        # TEN_POWERS_NUMBERS = [aa[0:7+p] for p in xrange(0,len(TEN_POWERS)*3,3)]
        elif number in TEN_POWERS_NUMBERS:
            return 'un ' + TEN_POWERS[len(number)-1]
     
     
     
        # We have to split the number in pieces of three digits.
        elif len(number) <= BIGGEST_DIGITS_NUMBER:
     
            li = [ printer(number[-q-9:-q-6].lstrip('0'))\
                   + ' ' + TEN_POWERS[q+6] + (number[-q-9:-q-6] not in ('001','1'))*'S'
                   for q in xrange(0,len(number)-6,3) if number[-q-9:-q-6]!='000' ]
            li.reverse()
     
            if number[-6:-3] in ('1','001'):
                li.append('MILLE')
            elif int(number[-6:-3]):
                li.append( (printer(number[-6:-3].lstrip('0')) + ' MILLE').replace('ts MILLE','t MILLE') )
     
            if int(number[-3:]):
                li.append( printer(number[-3:].lstrip('0')) )
     
            return ' '.join(li)
     
     
     
        else:
            raise ValueError('number = "' + str(number) + '" is too big.')
     
     
     
    ###############
    ###############
    ##           ##
    ## FOR TESTS ##
    ##           ##
    ###############
    ###############
     
    if __name__ == '__main__':
        import random
     
     
        # 0 = randomTest
        # 1 = calculs de temps sur 100 nombres et 100 repetitions
        # 2 resultats sur une selection de nombres
        # 4 temps en fonction de la longueur du nombre traduit
        choix = 1
     
     
        if choix==0: # randomTest:
            nbOfTest_in_each = 3
            g = ( random.randint(10**p,10**(p+1)-1) for p in xrange(0,BIGGEST_DIGITS_NUMBER)
                  for i in xrange(nbOfTest_in_each) )
            print '\n\n'.join( '* '+str(oneNumber)+'\t' + printer(oneNumber,checkValidity = True)
                               for oneNumber in g ) + '\n\n'
     
     
        if choix==1:
            g = (1580      ,4580      ,8080      ,7856      ,6587      ,5234      ,
                 29400     ,56890     ,45378     ,34565     ,87275     ,87266     ,
                 345627    ,676448    ,987635    ,897733    ,334559    ,939847    ,
                 5053054   ,9000658   ,9800727   ,3456486   ,1001980   ,1003801   ,
                 79003801  ,87080004  ,40000056  ,56700080  ,58008050  ,24555255  ,
                 700100000 ,123456081 ,340018480 ,380407080 ,345346366 ,236346366 ,
                 845534555 ,100033000 ,800000400 ,880905004 ,453223525 ,364574457 ,
                 3453453355,4353455555,9046571455,4773377777,8223467734,8564345523,
                 77764565445,
                 999999999999,
                 3778276346674,
                 28359872639829,
                 772387682736876,
                 1287815288647698,
                 16638273948723424,
                 726472365472545823,
                 8768176486787162869,
                 98719694691214212444,
                 989897169649819294981,
                 8728734827687142772933,
                 89894875982837582698299,
                 909697465868375676252342,
                 9978768762857628734824877,
                 87643768758254656545871877,
                 873247652833857475847874684,
                 8772874576354871878769692347,
                 87347617243716258417268587624,
                 676628572874916918625752837777,
                 8768726872864569700409849829848,
                 88762875876582787037047307982394,
                 987987129879192687876434798733144,
                 1098749868787629877917020090398123,
                 99879746875687268235234234234234666,
                 666283768476827683476263646709863455,
                 8787835876121721737128387182361355536,
                 91797712964162592903747041443454535314,
                 918961872648716949129891971927971976212,
                 9198169289798172871298379127937197390812,
                 98719761238716287619619918928798172987918,
                 891798129891872390710370170273071093091237,
                 10**21,10**24,10**27,'1'+30*'0','10'+16*'00',
                 10000000000,100000000000,
                 600080327103,500080759800,90000018400,9001000000000,
                 600000180000000076565543976,700000000180000000043976,
                 243546780874039872674836252308374664387,5*'10101',8*10**5+2*100**4,
                 '1000' + '0'*9,'6000' + '0'*13,'78954355'+4*'00'+'83',42*'9')
            print 'liste de nombres testee: '+str(len(list(g)))+' nombres'
            print 'temps pour 100 executions repetees'
            print '\ndefinitions des donnees de base:\n',t2-t1,'secondes'
            for yy in xrange(10):
                te =clock()
                for y in xrange(100):
                    for oneNumber in g:
                        x = printer(oneNumber,checkValidity = True)
                tf = clock()
                print "\ntemps d'execution des 100 repetitions sur les "+str(len(list(g)))+" nombres:"
                print (tf-te)
                print "temps d'execution moyen par nombre des 100 repetitions:"
                print (tf-te)/len(list(g))
     
        if choix==2:
            g = (0000000000000000,'0000000',0,
                 1,2,3,6,8,13,17,20,21,22,26,31,32,40,41,45,51,59,60,61,63,
                 70,71,73,77,80,81,82,90,91,96,98,
                 100,106,110,179,180,187,194,297,300,469,508,519,571,780,899,
                 1000,1580,29400,600530,900068,980077,
                 1001980,1003801,79003801,87080004,400000567,567000800,580080500,
                 67900000,67000000,67000001,
                 7001000000,12345678081,34001678480,380407000480,
                 1000000,80000000,880905004,
                 10000000000,100000000000,100000000010,999999999999,
                 1000000000000,1000000000000000,10000000000000000000,
                 10**21,10**24,10**27,'1'+30*'0','10'+16*'00',
                 600080327103,500080759800,90000018400,9001000000000,
                 600000180000000043976,700000000180000000043976,898000411000180000000043976,
                 243546780874039872674836252308374664387,5*'10101',8*10**5+2*100**4,
                 '1000' + '0'*9,'6000' + '0'*13,'78954355'+4*'00'+'83',
                 485000000000008760004560000000000003456,42*'9')    
            ecr = '\n\n'.join( '* '+str(oneNumber)+'\t' + printer(oneNumber,checkValidity = True)
                               for oneNumber in g ) + '\n\n'
            print ecr
     
     
        if choix==3:
            print
            for numb in (4,44,444,4444,44444,444444,4444444,44444444,444444444,4444444444,
                         44444444444,444444444444,4444444444444,44444444444444,444444444444444,
                         4444444444444444,44444444444444444,
                         444444444444444444,'444444444444444444',
                         4444444444444444444,'4444444444444444444'):
                te =clock()
                for y in xrange(100):
                    x = printer(numb,checkValidity = True)
                tf = clock()
                print len(str(numb)),tf-te


    Deuxièmement, j’ai comparé les vitesses de ce code et de ton code légèrement modifié, rambc (j’ai mis BIGGEST_NUMBER_DIGITS = max(TEN_POWERS) + 3 )

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    # -*- coding: cp1252 -*-
    from time import clock
     
    # This code only works with Python 3.
     
    # The following dictionnary will be used to name
    # a number by finding its pieces of three digits.
     
     
    t1 = clock()
    for xx in xrange(100):
     
        TEN_POWERS = {   3: "mille",
                         6: 'million'     ,  9: 'milliard'     ,
                        12: 'billion'     , 15: 'billiard'     ,
                        18: 'trillion'    , 21: 'trilliard'    ,
                        24: 'quadrillion' , 27: 'quadrilliard' ,
                        30: 'quintillion' , 33: 'quintilliard' ,
                        36: 'sextillion'  , 39: 'sextilliard'   }
     
        SPECIAL_NUMBERS_NAMES = {
            '0': "zéro",
            '1': "un",
            '2': "deux",
            '3': "trois",
            '4': "quatre",
            '5': "cinq",
            '6': "six",
            '7': "sept",
            '8': "huit",
            '9': "neuf",
     
            '10': "dix",
            '11': "onze",
            '12': "douze",
            '13': "treize",
            '14': "quatorze",
            '15': "quinze",
            '16': "seize",
     
            '20': "vingt",
            '30': "trente",
            '40': "quarante",
            '50': "cinquante",
            '60': "soixante",
            '70': "soixante-dix",
            '80': "quatre-vingt",
            '90': "quatre-vingt-dix",
     
            '100': "cent"
                                }
     
        # We add the powers of ten to SPECIAL_NUMBERS_NAMES.
        for onePower in TEN_POWERS:
            SPECIAL_NUMBERS_NAMES['1' + '0'*onePower] = TEN_POWERS[onePower]
     
        # We also find the biggest number of digits allowed.
        BIGGEST_NUMBER_DIGITS = max(TEN_POWERS) + 3
     
     
    t2 = clock()
     
     
     
     
     
     
    def printer(number, checkValidity = True):
        if checkValidity:
            try:
                number = int(number)
                kindOfNumber = 'integer'
    # We work with a string.
                number = str(number)
                checkValidity = False
            except:
                raise ValueError('number = "' + str(number) + '" must be an integer')
        else:
    # We clean unusefull zeros...
            number = str(int(number))
     
    # We have a base number.
        if number in SPECIAL_NUMBERS_NAMES:
            return SPECIAL_NUMBERS_NAMES[number]
     
    # We have a number less equal to 99.
    #
    # 0, 1, ... , 9 have been already treated.
    # 10, 11, 12, 13, 14, 15, 16 have been already treated.
    # 20, ... , 80 and 90 have been already treated.
        if len(number) == 2:
    # We have to take care of things like 76 and 94.
            if number[0] in ['7', '9']:
                unityName = printer( number = '1' + number[1],
                                     checkValidity = False )
                special = (int(number[0]) - 1)* 10
                decimalName = printer( number = special,
                                       checkValidity = False )
            else:
                unityName = printer(number[1])
                decimalName = printer( number = number[0] + '0',
                                       checkValidity = False )
     
            if number[1] == '1' and number[0] not in ['8', '9']:
                answer = '{0} et {1}'
            else:
                answer = '{0}-{1}'
     
            return answer.format(decimalName, unityName)
     
    # We have a number between 101 and 999.
    # 100 has been already treated.
        if len(number) == 3:
            centName = printer( number = '100',
                                checkValidity = False )
     
            if number[0] != '1':
                centName = printer( number = number[0],
                                    checkValidity = False ) \
                           + ' ' + centName
     
            decimalPartName = printer( number = number[1:],
                                       checkValidity = False )
     
            if decimalPartName == SPECIAL_NUMBERS_NAMES['0']:
    # Gramatical french boring rules...
                if number[0] != '1':
                    centName += 's'
                return centName
     
            return centName + ' ' + decimalPartName
     
    # We have to split the number in pieces of three digits.
        if len(number) <= BIGGEST_NUMBER_DIGITS:
            piecesOfDigits = []
            piecesOfNames = []
     
            while number:
                actualPieceOfDigits = number[-3:]
                piecesOfDigits.append( printer( number = actualPieceOfDigits,
                                                checkValidity = False ) )
                number = number[:-3]
     
            answer = ''
     
            for powerOfTen in range(len(piecesOfDigits)-1, -1, -1):
                actualPieceOfDigits = piecesOfDigits[powerOfTen]
     
                if actualPieceOfDigits != SPECIAL_NUMBERS_NAMES['0']:
                    if powerOfTen:
                        powerName = TEN_POWERS[powerOfTen*3]
     
                        if actualPieceOfDigits == SPECIAL_NUMBERS_NAMES['1']:
                            piecesOfNames.append( powerName )
     
                        else:
    # Gramatical french boring rules...
                            if powerName != SPECIAL_NUMBERS_NAMES['1000']:
                                powerName += 's'
     
     
                            if actualPieceOfDigits[-len('cents'):] == 'cents':
                                actualPieceOfDigits = actualPieceOfDigits[:-1]
     
                            piecesOfNames.append( actualPieceOfDigits + ' ' + powerName )
     
                    else:
                        piecesOfNames.append( actualPieceOfDigits )
     
            return ' '.join(piecesOfNames)
     
     
        raise ValueError('number = "' + str(number) + '" is too big.')
     
     
    ###############
    ###############
    ##           ##
    ## FOR TESTS ##
    ##           ##
    ###############
    ###############
     
    if __name__ == '__main__':
        import random
     
        randomTest = None
        nMin = 0
        nMax = 10**5
        nbOfTest = 20
     
        if randomTest:
            nMax += 1
            for i in range(nbOfTest):
                oneNumber = random.randint(nMin, nMax)
                print(str(oneNumber))
                print('\t' + printer(oneNumber))
     
        elif randomTest!=None:
            for oneNumber in test:
                print(str(oneNumber))
                print('\t' + printer(oneNumber))
     
     
     
     
        choix = 1
     
        if choix==1:
            g = (1580      ,4580      ,8080      ,7856      ,6587      ,5234      ,
                 29400     ,56890     ,45378     ,34565     ,87275     ,87266     ,
                 345627    ,676448    ,987635    ,897733    ,334559    ,939847    ,
                 5053054   ,9000658   ,9800727   ,3456486   ,1001980   ,1003801   ,
                 79003801  ,87080004  ,40000056  ,56700080  ,58008050  ,24555255  ,
                 700100000 ,123456081 ,340018480 ,380407080 ,345346366 ,236346366 ,
                 845534555 ,100033000 ,800000400 ,880905004 ,453223525 ,364574457 ,
                 3453453355,4353455555,9046571455,4773377777,8223467734,8564345523,
                 77764565445,
                 999999999999,
                 3778276346674,
                 28359872639829,
                 772387682736876,
                 1287815288647698,
                 16638273948723424,
                 726472365472545823,
                 8768176486787162869,
                 98719694691214212444,
                 989897169649819294981,
                 8728734827687142772933,
                 89894875982837582698299,
                 909697465868375676252342,
                 9978768762857628734824877,
                 87643768758254656545871877,
                 873247652833857475847874684,
                 8772874576354871878769692347,
                 87347617243716258417268587624,
                 676628572874916918625752837777,
                 8768726872864569700409849829848,
                 88762875876582787037047307982394,
                 987987129879192687876434798733144,
                 1098749868787629877917020090398123,
                 99879746875687268235234234234234666,
                 666283768476827683476263646709863455,
                 8787835876121721737128387182361355536,
                 91797712964162592903747041443454535314,
                 918961872648716949129891971927971976212,
                 9198169289798172871298379127937197390812,
                 98719761238716287619619918928798172987918,
                 891798129891872390710370170273071093091237,
                 10**21,10**24,10**27,'1'+30*'0','10'+16*'00',
                 10000000000,100000000000,
                 600080327103,500080759800,90000018400,9001000000000,
                 600000180000000076565543976,700000000180000000043976,
                 243546780874039872674836252308374664387,5*'10101',8*10**5+2*100**4,
                 '1000' + '0'*9,'6000' + '0'*13,'78954355'+4*'00'+'83',42*'9')
            print 'liste de nombres testee: '+str(len(list(g)))+' nombres'
            print 'temps pour 100 executions repetees'
            print '\ndefinitions des donnees de base:\n',t2-t1,'secondes'
            for yy in xrange(10):
                te =clock()
                for y in xrange(100):
                    for oneNumber in g:
                        x = printer(oneNumber)
                tf = clock()
                print "\ntemps d'execution des 100 repetitions sur les "+str(len(list(g)))+" nombres:",
                print (tf-te)
                print "temps d'execution moyen par nombre des 100 repetitions:",
                print (tf-te)/len(list(g))
     
        if choix==2:
            g = (0000000000000000,'0000000',0,
                 1,2,3,6,8,13,17,20,21,22,26,31,32,40,41,45,51,59,60,61,63,
                 70,71,73,77,80,81,82,90,91,96,98,
                 100,106,110,179,180,187,194,297,300,469,508,519,571,780,899,
                 1000,1580,29400,600530,900068,980077,
                 1001980,1003801,79003801,87080004,400000567,567000800,580080500,
                 67900000,67000000,67000001,
                 7001000000,12345678081,34001678480,380407000480,
                 1000000,80000000,880905004,
                 10000000000,100000000000,100000000010,999999999999,
                 1000000000000,1000000000000000,10000000000000000000,
                 10**21,10**24,10**27,'1'+30*'0','10'+16*'00',
                 600080327103,500080759800,90000018400,9001000000000,
                 600000180000000043976,700000000180000000043976,898000411000180000000043976,
                 243546780874039872674836252308374664387,5*'10101',8*10**5+2*100**4,
                 '1000' + '0'*9,'6000' + '0'*13,'78954355'+4*'00'+'83',
                 485000000000008760004560000000000003456,42*'9')    
            ecr = '\n\n'.join( '* '+str(oneNumber)+'\t' + printer(oneNumber)
                               for oneNumber in g ) + '\n\n'
            print ecr
     
     
        if choix==3:
            print
            for numb in (4,44,444,4444,44444,444444,4444444,44444444,444444444,4444444444,
                         44444444444,444444444444,4444444444444,44444444444444,444444444444444,
                         4444444444444444,44444444444444444,
                         444444444444444444,'444444444444444444',
                         4444444444444444444,'4444444444444444444'):
                te =clock()
                for y in xrange(100):
                    x = printer(numb)
                tf = clock()
                print len(str(numb)),tf-te



    Troisièmement, j’ai aussi regardé les temps que prennent les définitions préalables des données de base nécessaires aux codes avant leurs exécutions.

    Je me suis alors aperçu que l’insertion des centaines dans ADJCTV_NUMBERS que je faisais au début par un updating
    ADJCTV_NUMBERS.update( (str(100*h),ADJCTV_NUMBERS[str(h)]+' cents') for h in xrange(2,10) )
    prend beaucoup de temps.
    Je les ai alors écrites à la main directement dans le dictionnaire. Ça prend du temps une seule fois.

    J’ai aussi rusé en créant un tuple TEN_POWERS_NUMBERS pour éviter de créer un dictionnaire du genre
    TEN_POWERS_NUMBERS = dict( (str(10**p),TEN_POWERS[p])
    for p in TEN_POWERS )

    Rien que cette instruction me multipliait par 3 le temps de définition des données préalables.





    RÉSULTATS DE NOMBREUX TESTS
    (temps les plus petits obtenus = les plus révélateurs, temps pour 100 répétitions)


    code rambc
    définition des données: 0,00898
    exécution moyenne sur un nombre: 0,06970

    mon code
    définition des données: 0,01188
    exécution moyenne sur un nombre: 0,01975


    NB : tous les temps correspondent à 100 répétitions d’exécution pour avoir des temps pas trop petits



    Conclusions:

    Ma définition des données préalables est 32 % plus longue que celle de rambc

    Mon code diminue de 71 % le temps de traduction moyen d’un nombre

    Le temps de définition de données représente 13 % de la traduction moyenne d’un nombre dans le code de rambc.
    Il en représente 60 % dans mon code:
    normal puisque ce temps de définition augmente et que le temps de traduction d’un nombre diminue dans mon code.


    Pour traduire un seul nombre, en moyenne le code de rambc va mettre 0,078 ,
    le mien va mettre 0,031 soit 60 % de temps en moins .
    Plus on traduira de nombres, plus l’écart se creusera.


    Tout ça m’a donné pas mal de fil à détordre, mais je ne suis pas mécontent d’avoir passé autant de temps à en gagner.
    Ce qui me satisfait, c’est la meilleure lisibilité et compréhensibilité du code obtenu (je trouve). Et la confirmation de mon idée de départ.

  13. #13
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 751
    Points
    1 751
    Par défaut
    Merci pour toutes ces remarques.
    Malheureusement je n'aurais pas le temps d'étudier tout ceci avant la semaine prochaine mais c'est promis je le ferais.

    Au premier coup d'oeil, pour définir ton dictionnaire, tu devrais automatiser les définitions des clés '0...'.

    Pour le reste, je reviendrais ici la semaine prochaine.

    PS : je mets ci-dessous le code que j'ai fait avec trois systèmes pour nommer les nombres sans limite de taille. Ce code souffre de mal fonctionnement et reste un brouillon. Il ne corrige pas encore les 80 et compagnie.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    #coding=utf-8
    #!/usr/bin/env python
     
    #TODO million milliards
     
    # This code only works with Python 3.
     
    # There are three conventions used for very big numbers:
    #    1) The first one named "everyday" is the every day convention :
    # 132*10^9 = "cent trent-deux milliards"
    # 10^12 = "mille milliards"
    # 124*10^6 * 10*9 = "cent vingt-quatre millions de milliards" ... etc
    #    2) The secund convention named "chuquet" is defined here
    # (and it is very easy to learn):
    #        http://fr.wikipedia.org/wiki/Nom_des_grands_nombres#Famille_des_-llions
    # For number bigger or equal to 10^60 , we use a convention similar to the first one.
    #    3) The third convention named "rowlett" is defined here :
    #        http://fr.wikipedia.org/wiki/Nom_des_grands_nombres#Syst.C3.A8me_Gillion
    # For number bigger or equal to 10^90 , we use a convention similar to the first one.
    #
    # **NOTE :** we use to write "huit-cent-quatre" for "804".
    # The dashes are used ONLY for numbers between 101 and 999.
    # This is not the official convention but by doing this
    # we make the groups of three digits more visible.
     
    # The following dictionnary will be used to name
    # a number by finding its pieces of three digits.
    SPECIAL_TEN_POWERS = {
        3: "mille",
        6: "million"
                 }
     
    SPECIAL_NUMBERS_NAMES = {
        '0': "zéro",
        '1': "un",
        '2': "deux",
        '3': "trois",
        '4': "quatre",
        '5': "cinq",
        '6': "six",
        '7': "sept",
        '8': "huit",
        '9': "neuf",
     
        '10': "dix",
        '11': "onze",
        '12': "douze",
        '13': "treize",
        '14': "quatorze",
        '15': "quinze",
        '16': "seize",
     
        '20': "vingt",
        '30': "trente",
        '40': "quarante",
        '50': "cinquante",
        '60': "soixante",
        '70': "soixante-dix",
        '80': "quatre-vingt",
        '90': "quatre-vingt-dix",
     
        '100': "cent"
                            }
     
    # We add the power of ten to SPECIAL_NUMBERS_NAMES.
    # We also find the biggest number of digits allowed.
    for onePower in SPECIAL_TEN_POWERS:
        SPECIAL_NUMBERS_NAMES['1' + '0'*onePower] = SPECIAL_TEN_POWERS[onePower]
     
    BIG_NUMBERS_NAMES = {}
    BIG_NUMBERS_NAMES['everyday'] = {
        9: "milliard"           # 10^9
                     }
     
    BIG_NUMBERS_NAMES['chuquet'] = {
        12: "billion",           # 10^12
        18: "trillion",          # 10^18 ...
        24: "quadrillion",
        30: "quintillion",
        36: "sextillion",
        42: "septillion",
        48: "octillion",
        54: "nonillion",
        60: "decillion"
                    }
     
    BIG_NUMBERS_NAMES['rowlett'] = {
        9: "milliard",          # 10^9
        12: "tetrillion",       # 10^12
        15: "pentillion",       # 10^15 ...
        18: "hexillion",
        21: "eptillion",
        24: "oktillion",
        27: "ennillion",
        30: "dekillion",
        33: "hendekillion",
        36: "dodekillion",
        39: "trisdekillion",
        42: "tetradekillion",
        45: "pentadekillion",
        48: "hexadekillion",
        51: "heptadekillion",
        54: "oktadekillion",
        57: "enneadekillion",
     
        60: "icosillion",
        63: "icosihenillion",
        66: "icosidillion",
        69: "icositrillion",
        72: "icositetrillion",
        75: "icosipentillion",
        78: "icosihexillion",
        81: "icosiheptillion",
        84: "icosioktillion",
        87: "icosiennillion",
     
        90: "triacontillion"
                    }
     
    # We build the range to build so as to split big numbers.
    INDEX_TO_POWER = {}
    MAX_POWER = {}
    THE_BIGGER_NAME = {}
     
    for oneConvention in BIG_NUMBERS_NAMES:
        INDEX_TO_POWER[oneConvention] = sorted([ int(x) for x in BIG_NUMBERS_NAMES[oneConvention] ])
        powerOrdered = [6] + INDEX_TO_POWER[oneConvention]
        MAX_POWER[oneConvention] = powerOrdered[-1]
        THE_BIGGER_NAME[oneConvention] = BIG_NUMBERS_NAMES[oneConvention][ INDEX_TO_POWER[oneConvention][-1] ]
     
     
    ###############
    # THE FUNCTIONS
     
    #TODO   orderMagnitude à améliorer....
    def orderMagnitude(number):
        """
        ????
        """
        l = len(number) // 3
        i = len(number) - 3*l
     
        if i == 0:
            i += 3
            l -= 1
     
        return number[:i] + '0'*l*3
     
     
    def floor(number, tenPowerPrecision = 0):
        """
        This function changes the tenPowerPrecision right digits with zeros.
        """
        if type(tenPowerPrecision) != int or \
                tenPowerPrecision < 0:
            raise ValueError("""tenPowerPrecision = "' + str(tenPowerPrecision) + '" is not allowed.
     
    tenPowerPrecision can only be equal to a natural n with 10^n is the precision needed.
    """)
        number = str(number)
     
        if tenPowerPrecision > 0 and len(number) > tenPowerPrecision:
            number = number[:len(number) - tenPowerPrecision] + '0'*(tenPowerPrecision)
     
        return number
     
     
    def cleanInteger(number):
        """
        None is return when the number is not an integer.
        """
     
        number = str(number).replace(' ', '')
     
        test = number
        for i in range(10):
            test = test.replace(str(i), '')
     
        if test:
            return None
     
        return number
     
     
    def printer(number, firstCall = True, convention = "everyday"):
        if firstCall:
            if convention not in BIG_NUMBERS_NAMES:
                raise ValueError('convention = "' + str(convention) + '" is unknown.')
     
    # We work with a string.
            number = cleanInteger(number)
            if not number:
                raise ValueError('number = "' + str(number) + '" is not an integer.')
     
    # We clean unusefull zeros...
    #
    # REMARK : we know that we have a little integer.
        number = str(int(number))
     
        try:
            while number[0] == '':
                number = number[1:]
        except:
            number = '0'
     
    # We have a base number.
        if number in SPECIAL_NUMBERS_NAMES:
    # We have to take care of 10*6
            answer = SPECIAL_NUMBERS_NAMES[number]
     
            if answer == SPECIAL_NUMBERS_NAMES['1' + '0'*6]:
                answer = 'un ' + answer
     
            return answer
     
    # We have a number lower than 100.
    #
    # 0, 1, ... , 9 have been already treated.
    # 10, 11, 12, 13, 14, 15, 16 have been already treated.
    # 20, ... , 80 and 90 have been already treated.
        if len(number) == 2:
    # We have to take care of things like 76 and 94.
            if number[0] in ['7', '9']:
                unityName = printer( number = '1' + number[1],
                                     firstCall = False )
                special = (int(number[0]) - 1)* 10
                decimalName = printer( number = special,
                                       firstCall = False )
            else:
                unityName = printer( number[1],
                                     firstCall = False )
                decimalName = printer( number = number[0] + '0',
                                       firstCall = False )
     
            if number[1] == '1' and number[0] not in ['8', '9']:
                answer = '{0}-et-{1}'
            else:
                answer = '{0}-{1}'
     
            return answer.format(decimalName, unityName)
     
    # We have a number between 101 and 999.
    # 100 has been already treated.
        if len(number) == 3:
            centName = SPECIAL_NUMBERS_NAMES['100']
     
            if number[0] != '1':
                centName = printer( number = number[0],
                                    firstCall = False ) \
                           + '-' + centName
     
            decimalPartName = printer( number = number[1:],
                                       firstCall = False )
     
            if decimalPartName == SPECIAL_NUMBERS_NAMES['0']:
    # Gramatical french boring rules...
                if number[0] != '1':
                    centName += 's'
                return centName
     
            return centName + '-' + decimalPartName
     
    # We have a number between 1001 and 999 999.
        if len(number)  < 9:
            piecesOfDigits = []
            piecesOfNames = []
     
            while number:
                actualPieceOfDigits = number[-3:]
                piecesOfDigits.append( printer( number = actualPieceOfDigits,
                                                firstCall = False ) )
                number = number[:-3]
     
            answer = ''
     
            for powerOfTen in range(len(piecesOfDigits)-1, -1, -1):
                actualPieceOfDigits = piecesOfDigits[powerOfTen]
     
                if actualPieceOfDigits != SPECIAL_NUMBERS_NAMES['0']:
                    if powerOfTen:
                        powerName = SPECIAL_TEN_POWERS[powerOfTen*3]
     
                        if actualPieceOfDigits == SPECIAL_NUMBERS_NAMES['1']:
                            piecesOfNames.append( powerName )
     
                        else:
    # Gramatical french boring rules...
                            if powerName != SPECIAL_NUMBERS_NAMES['1000']:
                                powerName += 's'
     
     
                            if actualPieceOfDigits[-len('cents'):] == 'cents':
                                actualPieceOfDigits = actualPieceOfDigits[:-1]
     
                            piecesOfNames.append( actualPieceOfDigits + ' ' + powerName )
     
                    else:
                        piecesOfNames.append( actualPieceOfDigits )
     
            return ' '.join(piecesOfNames)
     
    # The number is at least 1O^6 and has at most 9 or 11 digits.
    #
    # WARNING !
    #    109 876543210 987654321  --->  everyday
    #    109876543 210987654321   --->  chuquet
    #
    # We have to split the number in pieces of three digits.
        index_to_power = INDEX_TO_POWER[convention]
     
        if len(number)  < index_to_power[0] + 1:
            underMillion = printer( number[-6:],
                                    firstCall = False )
            millionLike = printer( number[:-6],
                                   firstCall = False )
     
            answer = []
     
            if millionLike != SPECIAL_NUMBERS_NAMES['0']:
                answer.append( millionLike + ' ' + SPECIAL_NUMBERS_NAMES['1' + '0'*6] + 's' )
     
            if underMillion != SPECIAL_NUMBERS_NAMES['0']:
                answer.append( underMillion )
     
            return ' '.join(answer)
     
    # The number has mare than 9 digits.
    #
    # For example with the convention "everyday",
    # we build the following lits
    #        [['234567890'], ['345678901'], ['456789012'], ['123']]
    # associated to the number
    #        123 456789012 345678901 234567890
        piecesOfDigits = []
        max_power = MAX_POWER[convention]
     
        while number:
    #    123 456 789 012 345
    # becomes
    #    [['789012345'], ['123456']]
            piecesOfDigits.append(number[-max_power:])
            number = number[:-max_power]
     
    # We have to treat each pieces.
        listOfBlocks = []
        for onePiece in piecesOfDigits:
    # WARNING !
    #    109 876543210 987654321  --->  everyday
    #    109876543 210987654321   --->  chuquet
            if len(onePiece) < index_to_power[0] + 1:
                listOfBlocks.append([onePiece])
            else:
                thePieces = [ onePiece[-index_to_power[0]:] ]
     
                for i in range(1, len(index_to_power)):
                    if len(onePiece) >= int(index_to_power[i]):
                        oneBlock = onePiece[-index_to_power[i]: -index_to_power[i-1]]
                        if oneBlock:
                            thePieces.append(oneBlock)
                    else:
                        oneBlock = onePiece[:-index_to_power[i-1]]
                        if oneBlock:
                            thePieces.append(oneBlock)
                        break
     
                if len(onePiece) >= index_to_power[-1]:
                    oneBlock = onePiece[:-index_to_power[-1]]
                    if oneBlock:
                        thePieces.append(oneBlock)
     
                listOfBlocks.append(thePieces)
     
        namesOfThePieces = []
        plurial = []
     
        theBiggerName = THE_BIGGER_NAME[convention]
     
        for i_block in range(len(listOfBlocks) - 1, -1, -1):
            thePieces = listOfBlocks[i_block]
            nameOfTheCurrentPiece = []
     
            for i_piece in range(len(thePieces) - 1, -1, -1):
                onePiece = thePieces[i_piece]
     
                prefix = ''
                if len(onePiece) >= 6 and onePiece[-6:].replace('0', '') == '':
                    prefix = 'de '
     
                onePiece = printer( onePiece,
                                    firstCall = False,
                                    convention = convention )
     
                if onePiece != SPECIAL_NUMBERS_NAMES['0']:
                    if i_piece > 0:
                        if onePiece != SPECIAL_NUMBERS_NAMES['1']:
                            plurialToAdd = 's'
                        else:
                            plurialToAdd = ''
     
                        onePiece += ' ' + prefix \
                                  + BIG_NUMBERS_NAMES[convention][ INDEX_TO_POWER[convention][i_piece-1] ] \
                                  + plurialToAdd
     
                    nameOfTheCurrentPiece.append(onePiece)
     
            weHaveAPlurial = False
     
            for onePiece in nameOfTheCurrentPiece:
                if nameOfTheCurrentPiece.index(onePiece) != 0:
                    if onePiece != SPECIAL_NUMBERS_NAMES['0']:
                        weHaveAPlurial = True
                        break
                elif onePiece != SPECIAL_NUMBERS_NAMES['1']:
                    weHaveAPlurial = True
                    break
     
            plurial.append(weHaveAPlurial)
     
            nameOfTheCurrentPiece = ' '.join(nameOfTheCurrentPiece)
     
            namesOfThePieces.append(nameOfTheCurrentPiece)
     
    # To finish we have to treat cases like "milliards de milliards de milliards...".
        answer = []
     
        length = len(namesOfThePieces)
     
        for i in range(length):
            if namesOfThePieces[i] != SPECIAL_NUMBERS_NAMES['0']:
                if namesOfThePieces[i]:
                    if i < length - 1:
                        namesOfThePieces[i] += ' ' + theBiggerName
                        if plurial[i]:
                            namesOfThePieces[i] += 's'
     
                        namesOfThePieces[i] += (' de ' + theBiggerName + 's')*(length - i - 2)
     
                    answer.append(namesOfThePieces[i])
     
        return ' '.join(answer)
     
     
    ###############
    ###############
    ##           ##
    ## FOR TESTS ##
    ##           ##
    ###############
    ###############
     
    if __name__ == '__main__':
        myConvention ="everyday"
    #    myConvention ="rowlett"
    #    myConvention ="chuquet"
     
        mytenPowerPrecision = 0
    #    mytenPowerPrecision = 3
     
        onlyTheOrderOfMagnitude = False
        onlyTheOrderOfMagnitude = True
     
        randomTest = True
        randomTest = False
        nMin = 0
        nMax = 10000000
        nbOfTest = 5
     
        test = [
            4,
            400,
            5000,
            184,
            1840,
            18400,
            184000,
            400567,
            91,
            71,
            "1 200 000 567",
            "123 456 789",
            "123 456 789 012 345",
            10**6,
            134*10**6,
            10**9,
            '1000' + '0'*9,
    #### mille millards
            '1' + '0'*6 + '0'*9,
    #### un million de millards
            "7" + '0'*(9*4),
    #### sept milliards de milliards de milliards
               ]
     
        if randomTest:
            import random
            nMax += 1
            test = [random.randint(nMin, nMax) for x in range(nbOfTest)]
     
        for oneNumber in test:
            print(str(oneNumber))
     
            oneNumber = str(oneNumber).strip().replace(' ', '')
     
            if onlyTheOrderOfMagnitude:
                oneNumber = orderMagnitude(number = oneNumber)
                print('\torder of magnitude : ' + str(oneNumber))
     
            elif mytenPowerPrecision:
                oneNumber = floor(number = oneNumber,
                                  tenPowerPrecision = mytenPowerPrecision)
                print('\tfloor : ' + str(oneNumber))
     
            print('\t' + printer( number = oneNumber,
                                  convention = myConvention ))

  14. #14
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 751
    Points
    1 751
    Par défaut
    J'ai fait des modifications à l'aide des remarques ci-dessus. C'est loin d'être parfait mais je n'ai pas le temps.

    Le fichier congif_fr.py :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    # -*- coding: utf-8 -*-
    #!/usr/bin/env python
     
    SPECIAL_NUMBERS_NAMES = {
        '0': "zéro",
        '1': "un",
        '2': "deux",
        '3': "trois",
        '4': "quatre",
        '5': "cinq",
        '6': "six",
        '7': "sept",
        '8': "huit",
        '9': "neuf",
     
        '10': "dix",
        '11': "onze",
        '12': "douze",
        '13': "treize",
        '14': "quatorze",
        '15': "quinze",
        '16': "seize",
     
        '20': "vingt",
        '30': "trente",
        '40': "quarante",
        '50': "cinquante",
        '60': "soixante",
        '70': "soixante-dix",
        '80': "quatre-vingts",
        '90': "quatre-vingt-dix",
     
        '100': "cent"
                            }
     
    TEN_POWERS_NAMES = {}
     
    TEN_POWERS_NAMES['everyday'] = {
        3: "mille",             # 10^3
        6: "million",           # 10^6
        9: "milliard"           # 10^9
                     }
     
    TEN_POWERS_NAMES['chuquet'] = {
        3: "mille",             # 10^3
        6: "million",           # 10^6
        12: "billion",          # 10^12
        18: "trillion",         # 10^18 ...
        24: "quadrillion",
        30: "quintillion",
        36: "sextillion",
        42: "septillion",
        48: "octillion",
        54: "nonillion",
        60: "decillion"
                    }
     
    TEN_POWERS_NAMES['rowlett'] = {
        3: "mille",             # 10^3
        6: "million",           # 10^6
        9: "milliard",          # 10^9
        12: "tetrillion",       # 10^12
        15: "pentillion",       # 10^15 ...
        18: "hexillion",
        21: "eptillion",
        24: "oktillion",
        27: "ennillion",
        30: "dekillion",
        33: "hendekillion",
        36: "dodekillion",
        39: "trisdekillion",
        42: "tetradekillion",
        45: "pentadekillion",
        48: "hexadekillion",
        51: "heptadekillion",
        54: "oktadekillion",
        57: "enneadekillion",
     
        60: "icosillion",
        63: "icosihenillion",
        66: "icosidillion",
        69: "icositrillion",
        72: "icositetrillion",
        75: "icosipentillion",
        78: "icosihexillion",
        81: "icosiheptillion",
        84: "icosioktillion",
        87: "icosiennillion",
     
        90: "triacontillion"
                    }
    Le fichier qui fait le travail.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    # -*- coding: utf-8 -*-
    #!/usr/bin/env python
     
    # **NOTES :** good improvings have been proposed in the following page.
    #    http://www.developpez.net/forums/d864956/autres-langages/python-zope/general-python/ecrire-nombre-toutes-lettres
     
    # There are three conventions used for very big numbers:
    #
    #    1) The first one named "everyday" is the every day convention :
    # 132*10^9 = "cent-trent-deux milliards"
    # 10^12 = "mille milliards"
    # 124*10^6 * 10*9 = "cent-vingt-quatre millions de milliards" ... etc
    #
    #    2) The secund convention named "chuquet", which is very easy to learn,
    # is defined in the following link :
    #        http://fr.wikipedia.org/wiki/Nom_des_grands_nombres#Famille_des_-llions
    # For number bigger or equal to 10^60 , we use a convention similar to the first one.
    #
    #    3) The third convention named "rowlett" is defined here :
    #        http://fr.wikipedia.org/wiki/Nom_des_grands_nombres#Syst.C3.A8me_Gillion
    # For number bigger or equal to 10^90 , we use a convention similar to the first one.
    #
    # **NOTE :** we use to write "huit-cent-quatre" for "804".
    # The dashes are used ONLY for numbers between 101 and 999.
    # This is not the official convention but by doing this
    # we make the groups of three digits more visible.
     
    import config_fr
     
    SPECIAL_NUMBERS_NAMES = config_fr.SPECIAL_NUMBERS_NAMES
    TEN_POWERS_NAMES = config_fr.TEN_POWERS_NAMES
     
    # We add 01, 02, 03,...
    for i in range(0,10):
        SPECIAL_NUMBERS_NAMES['0' + str(i)] = SPECIAL_NUMBERS_NAMES[str(i)]
     
    # SPECIFIC NAMES FOR FRENCH == START
    #
    # We add 'dix-sept', 'dix-huit' and 'dix-neuf'.
    for i in range(7, 10):
        SPECIAL_NUMBERS_NAMES['1' + str(i)] = "dix-" + SPECIAL_NUMBERS_NAMES[str(i)] 
     
    # We add 'vingt-et-un', ... , 'soixante-et-un'.
    for i in range(2, 7):
        SPECIAL_NUMBERS_NAMES[str(i) + '1'] = SPECIAL_NUMBERS_NAMES[str(i) + '0'] + "-et-un" 
     
    # We add 'soixante-et-onze'.
    SPECIAL_NUMBERS_NAMES['71'] = "soixante-et-onze"
     
    # We add 'deux cents', 'trois cents', ... such as 
    # to not treat the very boring gramatical rules.
    for i in range(2, 10):
        SPECIAL_NUMBERS_NAMES[str(i) + '00'] = SPECIAL_NUMBERS_NAMES[str(i)] + "-cents" 
     
    # For "trente-..." and co.
    TEN_PREFIXES = {}
    for i in range(2, 10):
        if i == 7:
            TEN_PREFIXES[str(i)] = SPECIAL_NUMBERS_NAMES[str(i-1) + '0'] + "-" 
        elif i == 8:
            TEN_PREFIXES[str(i)] = SPECIAL_NUMBERS_NAMES[str(i) + '0'][:-1] + "-"
        elif i == 9:
            TEN_PREFIXES[str(i)] = SPECIAL_NUMBERS_NAMES[str(i-1) + '0'][:-1] + "-"  
        else:
            TEN_PREFIXES[str(i)] = SPECIAL_NUMBERS_NAMES[str(i) + '0'] + "-" 
     
    #
    # SPECIFIC NAMES FOR FRENCH == END
     
     
    # We build the range to build so as to split big numbers.
    INDEX_TO_POWER = {}
    MAX_POWER = {}
    THE_BIGGER_NAME = {}
     
    for oneConvention in TEN_POWERS_NAMES:
        INDEX_TO_POWER[oneConvention] = sorted([ x for x in TEN_POWERS_NAMES[oneConvention] if x not in [3, 6] ])
        powerOrdered = [6] + INDEX_TO_POWER[oneConvention]
        MAX_POWER[oneConvention] = powerOrdered[-1]
        THE_BIGGER_NAME[oneConvention] = TEN_POWERS_NAMES[oneConvention][ INDEX_TO_POWER[oneConvention][-1] ]
     
     
    ###############
    # THE FUNCTIONS
     
    #TODO   orderMagnitude à améliorer....
    def orderMagnitude(number):
        """
        For example, 123456 becomes 123000, and 12345 becomes 12000 
        """
        l = len(number) // 3
        i = len(number) - 3*l
     
        if i == 0:
            i += 3
            l -= 1
     
        return number[:i] + '0'*l*3
     
     
    def floor(number, tenPowerPrecision = 0):
        """
        This function changes the tenPowerPrecision right digits with zeros.
        """
        if type(tenPowerPrecision) != int or \
                tenPowerPrecision < 0:
            raise ValueError("""tenPowerPrecision = "' + str(tenPowerPrecision) + '" is not allowed.
     
    tenPowerPrecision can only be equal to a natural n with 10^n is the precision needed.
    """)
        number = str(number)
     
        if tenPowerPrecision > 0 and len(number) > tenPowerPrecision:
            number = number[:len(number) - tenPowerPrecision] + '0'*(tenPowerPrecision)
     
        return number
     
     
    def cleanInteger(number):
        """
        None is return when the number is not an integer.
        """
     
        number = str(number).replace(' ', '')
     
        test = number
        for i in range(10):
            test = test.replace(str(i), '')
     
        if test:
            return None
     
        return number
     
     
    def printer(number, checkValidity = True, convention = "everyday"):
        if checkValidity:
            if convention not in TEN_POWERS_NAMES:
                raise ValueError('convention = "' + str(convention) + '" is unknown.')
     
    # We work with a string.
            number = cleanInteger(number)
            if not number:
                raise ValueError('number = "' + str(number) + '" is not an integer.')
     
    # We have directly the name of the number.
        if number in SPECIAL_NUMBERS_NAMES:
    # We have to take care of 10*6
            answer = SPECIAL_NUMBERS_NAMES[number]
     
            if answer == TEN_POWERS_NAMES[convention][6]:
                answer = 'un ' + answer
     
            return answer
     
    # We have a number lower than 100.
    #
    # 0, 1, ... , 9 have been already treated.
    # 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 have been already treated.
    # 20, 21, 30, 31, ... , 70, 71, 80 and 90 have been already treated.
        if len(number) == 2:
            if number[0] in ['7', '9']:
                return  TEN_PREFIXES[number[0]] + SPECIAL_NUMBERS_NAMES['1' + number[1]]
            else:
                return  TEN_PREFIXES[number[0]] + SPECIAL_NUMBERS_NAMES[number[1]]
     
    # We have a number between 101 and 999.
    #
    # 100, 200, 300, ... , 800 and 900 has been already treated.
    # So we do not have to take care of french boring rules.
        if len(number) == 3:
            if number[0] == '0':
                centName = ''
            else:
                centName = SPECIAL_NUMBERS_NAMES['100']
                if number[0] != '1':
                    centName = SPECIAL_NUMBERS_NAMES[number[0]] + '-' + centName
                centName += '-'
     
            return centName + printer( number = number[1:],
                                       checkValidity = False )
     
    #TODO : clean the code for a number between 1000 and 999 999 999
    # We have a number between 1000 and 999 999 999.
        ten_powers_names = TEN_POWERS_NAMES[convention]
     
        if len(number)  < 10:
            piecesOfDigits = []
            piecesOfNames = []
     
            while number:
    # number[-3:] is the actual piece of digits.
                piecesOfDigits.append( printer( number = number[-3:],
                                                checkValidity = False ) )
                number = number[:-3]
     
            answer = ''
     
            for powerOfTen in range(len(piecesOfDigits)-1, -1, -1):
                actualPieceOfDigits = piecesOfDigits[powerOfTen]
     
                if actualPieceOfDigits != SPECIAL_NUMBERS_NAMES['0']:
                    if powerOfTen:
                        powerName = ten_powers_names[powerOfTen*3]
     
                        if actualPieceOfDigits == SPECIAL_NUMBERS_NAMES['1']:
                            if powerName == ten_powers_names[6]:
                                piecesOfNames.append( SPECIAL_NUMBERS_NAMES['1'] + ' ' + powerName )
                            else:
                                piecesOfNames.append( powerName )
     
                        else:
    # Gramatical french boring rules for 'mille' like in 'deux mille'.
                            if powerName != ten_powers_names[3]:
                                powerName += 's'
     
                            piecesOfNames.append( actualPieceOfDigits + ' ' + powerName )
     
                    else:
                        piecesOfNames.append( actualPieceOfDigits )
     
            answer = ' '.join(piecesOfNames)
     
    # Gramatical french boring rules for 'cent' like in 'quatre-cent mille'.
            answer = answer.replace('cents ', 'cent ')
     
            return answer
     
    #TODO : clean the code for a number with more than 9 digits
     
    # The number has more than 9 digits (there are differences
    # at this step between the different conventions).
    #
    # For example with the convention "everyday",
    # we build the following lits
    #        [['234567890'], ['345678901'], ['456789012'], ['123']]
    # associated to the number
    #        123 456789012 345678901 234567890
        index_to_power = INDEX_TO_POWER[convention]
        max_power = MAX_POWER[convention]
        theBiggerName = THE_BIGGER_NAME[convention]
     
        piecesOfDigits = []
     
        while number:
    #    123 456 789 012 345
    # becomes
    #    [['789012345'], ['123456']]
            piecesOfDigits.append(number[-max_power:])
            number = number[:-max_power]
     
    # We have to treat each pieces.
        listOfBlocks = []
        for onePiece in piecesOfDigits:
    # WARNING !
    #    109 876543210 987654321  --->  everyday
    #    109876543 210987654321   --->  chuquet
            if len(onePiece) < index_to_power[0] + 1:
                listOfBlocks.append([onePiece])
            else:
                thePieces = [ onePiece[-index_to_power[0]:] ]
     
                for i in range(1, len(index_to_power)):
                    if len(onePiece) >= int(index_to_power[i]):
                        oneBlock = onePiece[-index_to_power[i]: -index_to_power[i-1]]
                        if oneBlock:
                            thePieces.append(oneBlock)
                    else:
                        oneBlock = onePiece[:-index_to_power[i-1]]
                        if oneBlock:
                            thePieces.append(oneBlock)
                        break
     
                if len(onePiece) >= index_to_power[-1]:
                    oneBlock = onePiece[:-index_to_power[-1]]
                    if oneBlock:
                        thePieces.append(oneBlock)
     
                listOfBlocks.append(thePieces)
     
        namesOfThePieces = []
        plurial = []
     
        for i_block in range(len(listOfBlocks) - 1, -1, -1):
            thePieces = listOfBlocks[i_block]
            nameOfTheCurrentPiece = []
     
            for i_piece in range(len(thePieces) - 1, -1, -1):
                onePiece = thePieces[i_piece]
     
                prefix = ''
                if len(onePiece) >= 6 and onePiece[-6:].replace('0', '') == '':
                    prefix = 'de '
     
                onePiece = printer( onePiece,
                                    checkValidity = False,
                                    convention = convention )
     
                if onePiece != SPECIAL_NUMBERS_NAMES['0']:
                    if i_piece > 0:
                        if onePiece != SPECIAL_NUMBERS_NAMES['1']:
                            plurialToAdd = 's'
                        else:
                            plurialToAdd = ''
     
                        onePiece += ' ' + prefix \
                                  + TEN_POWERS_NAMES[convention][ INDEX_TO_POWER[convention][i_piece-1] ] \
                                  + plurialToAdd
     
                    nameOfTheCurrentPiece.append(onePiece)
     
            weHaveAPlurial = False
     
            for onePiece in nameOfTheCurrentPiece:
                if nameOfTheCurrentPiece.index(onePiece) != 0:
                    if onePiece != SPECIAL_NUMBERS_NAMES['0']:
                        weHaveAPlurial = True
                        break
                elif onePiece != SPECIAL_NUMBERS_NAMES['1']:
                    weHaveAPlurial = True
                    break
     
            plurial.append(weHaveAPlurial)
     
            nameOfTheCurrentPiece = ' '.join(nameOfTheCurrentPiece)
     
            namesOfThePieces.append(nameOfTheCurrentPiece)
     
    # To finish we have to treat cases like "milliards de milliards de milliards...".
        answer = []
     
        length = len(namesOfThePieces)
     
        for i in range(length):
            if namesOfThePieces[i] != SPECIAL_NUMBERS_NAMES['0']:
                if namesOfThePieces[i]:
                    if i < length - 1:
                        namesOfThePieces[i] += ' ' + theBiggerName
                        if plurial[i]:
                            namesOfThePieces[i] += 's'
     
                        namesOfThePieces[i] += (' de ' + theBiggerName + 's')*(length - i - 2)
     
                    answer.append(namesOfThePieces[i])
     
     
        answer = ' '.join(answer)
     
    # We have to take care of cases like "un million DE milliards".
        for power in ten_powers_names:
            answer = answer.replace('million ' + ten_powers_names[power],
                                    'million de ' + ten_powers_names[power])
            answer = answer.replace('millions ' + ten_powers_names[power],
                                    'millions de ' + ten_powers_names[power])
     
        return answer
     
     
    ###############
    ###############
    ##           ##
    ## FOR TESTS ##
    ##           ##
    ###############
    ###############
     
    if __name__ == '__main__':
        myConvention ="everyday"
    #    myConvention ="rowlett"
    #    myConvention ="chuquet"
     
        mytenPowerPrecision = 0
    #    mytenPowerPrecision = 3
     
        onlyTheOrderOfMagnitude = False
    #    onlyTheOrderOfMagnitude = True
     
        randomTest = True
        randomTest = False
        nMin = 0
        nMax = 100000000
        nbOfTest = 5
     
        test = [
            4,
            400, 5000,
            107,        
            80, 1080,
            91, 71,
            184, 1840, 18400, 184000,
            400567,
            "1 200 000 567",
            "123 456 789",
            "123 456 789 012 345",
            10**6, 134*10**6, 10**9, "1000" + '0'*9,
    #### mille millards
            "1" + '0'*6 + '0'*9,
    #### un million de millards
            "7" + '0'*(9*4),
    #### sept milliards de milliards de milliards
               ]
     
        if randomTest:
            import random
            nMax += 1
            test = [random.randint(nMin, nMax) for x in range(nbOfTest)]
     
        for oneNumber in test:
            print(str(oneNumber))
     
            oneNumber = str(oneNumber).strip().replace(' ', '')
     
            if onlyTheOrderOfMagnitude:
                oneNumber = orderMagnitude(number = oneNumber)
                print('\torder of magnitude : ' + str(oneNumber))
     
            elif mytenPowerPrecision:
                oneNumber = floor(number = oneNumber,
                                  tenPowerPrecision = mytenPowerPrecision)
                print('\tfloor : ' + str(oneNumber))
     
            print('\t' + printer( number = oneNumber,
                                  convention = myConvention ))
    PS : je posterais plus tard le script associé qui fait le travail inverse. Par exemple "troi cen quatr", les fautes sont volontaires, devient "304". Il faut que je fasse quelques mises à jour.

  15. #15
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 751
    Points
    1 751
    Par défaut
    Citation Envoyé par spirzouf Voir le message
    Ce que tu as fait est a priori exactement ce que je recherche
    Si tu es d'accord pour mettre ton code en licence Creative Commons BY-NC-SA ou compatible, cela m'intéresse énormément. Je pourrai en effet l'intégrer à mon projet de logiciel d'entrainement au calcul que je développe en ce moment (pour mon niveau 4 de difficulté, voir ici)
    Utilises le script en indiquant sa provenance, à savoir ce forum. Cela me convient. De toute façon, ce script reste un brouillon pour moi.

  16. #16
    Membre régulier
    Homme Profil pro
    Etudiant CNAM (DIE20)
    Inscrit en
    Janvier 2010
    Messages
    151
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant CNAM (DIE20)

    Informations forums :
    Inscription : Janvier 2010
    Messages : 151
    Points : 97
    Points
    97
    Par défaut
    Citation Envoyé par rambc Voir le message
    Utilises le script en indiquant sa provenance, à savoir ce forum. Cela me convient. De toute façon, ce script reste un brouillon pour moi.
    Merci
    De toute façon je me suis pas mal renseigné sur les licences (du côté de framasoft), et quand il sera terminé, je vais passer mon soft en GPL comme tu le souhaitais initialement pour ton code.
    (NB : je ne l'utiliserai pas tout de suite, j'ai encore pas mal de chose à finir avant l'option d'affichage en lettres).

  17. #17
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 751
    Points
    1 751
    Par défaut
    Bon j'ai pris le temps de nettoyer et simplifier le code pour Python 3. Il devient lisible maintenant. J'avais fait des complications inutiles.

    J'ai aussi intégré les remarques d'equyem. Ceci donne un code plus générique. Le code pour la langue anglaise devient immédiate ou presque car les spécificités grammaticales françaises sont soit gérée via une ligne isolée de code, soit via l'ajout de clés aux dictionnaires. Merci equyem !

    config_fr.py
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    #!/usr/bin/env python
     
    SPECIAL_NUMBERS_NAMES = {
        '0': "zéro",
        '1': "un",
        '2': "deux",
        '3': "trois",
        '4': "quatre",
        '5': "cinq",
        '6': "six",
        '7': "sept",
        '8': "huit",
        '9': "neuf",
     
        '10': "dix",
        '11': "onze",
        '12': "douze",
        '13': "treize",
        '14': "quatorze",
        '15': "quinze",
        '16': "seize",
     
        '20': "vingt",
        '30': "trente",
        '40': "quarante",
        '50': "cinquante",
        '60': "soixante",
        '70': "soixante-dix",
        '80': "quatre-vingts",
        '90': "quatre-vingt-dix",
     
        '100': "cent"
                            }
     
    TEN_POWERS_NAMES = {}
     
    # 10^3 and 10 9 are given at the end.
     
    TEN_POWERS_NAMES['everyday'] = {
        9: "milliard"           # 10^9
                                   }
     
    TEN_POWERS_NAMES['chuquet'] = {
        12: "billion",          # 10^12
        18: "trillion",         # 10^18 ...
        24: "quadrillion",
        30: "quintillion",
        36: "sextillion",
        42: "septillion",
        48: "octillion",
        54: "nonillion",
        60: "decillion"
                                  }
     
    TEN_POWERS_NAMES['rowlett'] = {
        9: "milliard",          # 10^9
        12: "tetrillion",       # 10^12
        15: "pentillion",       # 10^15 ...
        18: "hexillion",
        21: "eptillion",
        24: "oktillion",
        27: "ennillion",
        30: "dekillion",
        33: "hendekillion",
        36: "dodekillion",
        39: "trisdekillion",
        42: "tetradekillion",
        45: "pentadekillion",
        48: "hexadekillion",
        51: "heptadekillion",
        54: "oktadekillion",
        57: "enneadekillion",
     
        60: "icosillion",
        63: "icosihenillion",
        66: "icosidillion",
        69: "icositrillion",
        72: "icositetrillion",
        75: "icosipentillion",
        78: "icosihexillion",
        81: "icosiheptillion",
        84: "icosioktillion",
        87: "icosiennillion",
     
        90: "triacontillion"
                                  }
     
    for oneConvention in ['everyday', 'chuquet', 'rowlett']:
        TEN_POWERS_NAMES[oneConvention][3] = "mille"
        TEN_POWERS_NAMES[oneConvention][6] = "million"
    integerToWords_fr.py
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    #!/usr/bin/env python
     
    # **NOTES :** good improvings have been proposed in the following page.
    #    http://www.developpez.net/forums/d864956/autres-langages/python-zope/general-python/ecrire-nombre-toutes-lettres
     
    # There are three conventions used for very big numbers:
    #
    #    1) The first one named "everyday" is the every day convention :
    # 132*10^9 = "cent-trent-deux milliards"
    # 10^12 = "mille milliards"
    # 124*10^6 * 10*9 = "cent-vingt-quatre millions de milliards" ... etc
    #
    #    2) The secund convention named "chuquet", which is very easy to learn,
    # is defined in the following link :
    #        http://fr.wikipedia.org/wiki/Nom_des_grands_nombres#Famille_des_-llions
    # For number bigger or equal to 10^60 , we use a convention similar to the first one.
    #
    #    3) The third convention named "rowlett" is defined here :
    #        http://fr.wikipedia.org/wiki/Nom_des_grands_nombres#Syst.C3.A8me_Gillion
    # For number bigger or equal to 10^90 , we use a convention similar to the first one.
    #
    # **NOTE :** we use to write "huit-cent-quatre" for "804".
    # The dashes are used ONLY for numbers between 101 and 999.
    # This is not the official convention but by doing this
    # we make the groups of three digits more visible.
     
    import config_fr
     
    SPECIAL_NUMBERS_NAMES = config_fr.SPECIAL_NUMBERS_NAMES
    TEN_POWERS_NAMES = config_fr.TEN_POWERS_NAMES
     
    # We add 01, 02, 03,...
    for i in range(0,10):
        SPECIAL_NUMBERS_NAMES['0' + str(i)] = SPECIAL_NUMBERS_NAMES[str(i)]
     
     
    ####################################
    # SPECIFIC NAMES FOR FRENCH == START
    #
    # We add 'dix-sept', 'dix-huit' and 'dix-neuf'.
    for i in range(7, 10):
        SPECIAL_NUMBERS_NAMES['1' + str(i)] = "dix-" + SPECIAL_NUMBERS_NAMES[str(i)]
     
    # We add 'vingt-et-un', ... , 'soixante-et-un'.
    for i in range(2, 7):
        SPECIAL_NUMBERS_NAMES[str(i) + '1'] = SPECIAL_NUMBERS_NAMES[str(i) + '0'] + "-et-un"
     
    # We add 'soixante-et-onze'.
    SPECIAL_NUMBERS_NAMES['71'] = "soixante-et-onze"
     
    # We add 'deux cents', 'trois cents', ... such as
    # to not treat the very boring gramatical rules.
    for i in range(2, 10):
        SPECIAL_NUMBERS_NAMES[str(i) + '00'] = SPECIAL_NUMBERS_NAMES[str(i)] + "-cents"
     
    # For "trente-..." and co.
    TEN_PREFIXES = {}
    for i in range(2, 10):
        if i == 7:
            TEN_PREFIXES[str(i)] = SPECIAL_NUMBERS_NAMES[str(i-1) + '0'] + "-"
        elif i == 8:
            TEN_PREFIXES[str(i)] = SPECIAL_NUMBERS_NAMES[str(i) + '0'][:-1] + "-"
        elif i == 9:
            TEN_PREFIXES[str(i)] = SPECIAL_NUMBERS_NAMES[str(i-1) + '0'][:-1] + "-"
        else:
            TEN_PREFIXES[str(i)] = SPECIAL_NUMBERS_NAMES[str(i) + '0'] + "-"
     
    #
    # SPECIFIC NAMES FOR FRENCH == END
    ##################################
     
     
    # We build the range to build so as to split big numbers.
    THE_POWERS = {}
    MAX_POWER = {}
    THE_BIGGER_NAME = {}
     
    for oneConvention in TEN_POWERS_NAMES:
    # We add zero so as to facilitate the procedures.
        THE_POWERS[oneConvention] = sorted([0] + [ x for x in TEN_POWERS_NAMES[oneConvention] if x != 3 ])
        MAX_POWER[oneConvention] = THE_POWERS[oneConvention][-1]
        THE_BIGGER_NAME[oneConvention] = TEN_POWERS_NAMES[oneConvention][ THE_POWERS[oneConvention][-1] ]
     
     
    ###############
    # THE FUNCTIONS
     
    #TODO   orderMagnitude à améliorer....
    def orderMagnitude(number):
        """
        For example, 123456 becomes 123000, and 12345 becomes 12000
        """
        l = len(number) // 3
        i = len(number) - 3*l
     
        if i == 0:
            i += 3
            l -= 1
     
        return number[:i] + '0'*l*3
     
     
    def floor(number, tenPowerPrecision = 0):
        """
        This function changes the tenPowerPrecision right digits with zeros.
        """
        if type(tenPowerPrecision) != int or \
                tenPowerPrecision < 0:
            raise ValueError("""tenPowerPrecision = "' + str(tenPowerPrecision) + '" is not allowed.
     
    tenPowerPrecision can only be equal to a natural n with 10^n is the precision needed.
    """)
        number = str(number)
     
        if tenPowerPrecision > 0 and len(number) > tenPowerPrecision:
            number = number[:len(number) - tenPowerPrecision] + '0'*(tenPowerPrecision)
     
        return number
     
     
    def cleanInteger(number):
        """
        None is return when the number is not an integer.
        """
     
        number = str(number).replace(' ', '')
     
        test = number
        for i in range(10):
            test = test.replace(str(i), '')
     
        if test:
            return None
     
        return number
     
     
    def boringFrenchGrammaticalRuleForCENT(litteralNumber):
        if litteralNumber[-5:] == 'cents':
            litteralNumber = litteralNumber[:-1]
        return litteralNumber
     
     
    def printer(number, checkValidity = True, convention = "everyday"):
        if checkValidity:
            if convention not in TEN_POWERS_NAMES:
                raise ValueError('convention = "' + str(convention) + '" is unknown.')
     
    # We work with a string.
            number = cleanInteger(number)
            if not number:
                raise ValueError('number = "' + str(number) + '" is not an integer.')
     
    # We have directly the name of the number.
        if number in SPECIAL_NUMBERS_NAMES:
    # We have to take care of 10*6
            answer = SPECIAL_NUMBERS_NAMES[number]
     
            if answer == TEN_POWERS_NAMES[convention][6]:
                answer = 'un ' + answer
     
            return answer
     
    # We have a number lower than 100.
    #
    # 0, 1, ... , 9 have been already treated.
    # 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 have been already treated.
    # 20, 21, 30, 31, ... , 70, 71, 80 and 90 have been already treated.
        if len(number) == 2:
            if number[0] in ['7', '9']:
                return  TEN_PREFIXES[number[0]] + SPECIAL_NUMBERS_NAMES['1' + number[1]]
            else:
                return  TEN_PREFIXES[number[0]] + SPECIAL_NUMBERS_NAMES[number[1]]
     
    # We have a number between 101 and 999.
    #
    # 100, 200, 300, ... , 800 and 900 has been already treated.
    # So we do not have to take care of french boring rules.
        if len(number) == 3:
            if number[0] == '0':
                hundredName = ''
            else:
                hundredName = SPECIAL_NUMBERS_NAMES['100']
                if number[0] != '1':
                    hundredName = SPECIAL_NUMBERS_NAMES[number[0]] + '-' + hundredName
                hundredName += '-'
     
            return hundredName + printer( number = number[1:],
                                       checkValidity = False )
     
    # We begin to treat number bigger than 1000.
        ten_powers_names = TEN_POWERS_NAMES[convention]
     
    # We have a number between 1000 and 999 999.
    #
    # We do that because later, "6 digits" is the bigger size of
    # the "intermediate" part like "77" and "124 345" in 77 124 345 000 000.
        if len(number) <= 6:
            hundredPart = printer( number = number[-3:],
                                   checkValidity = False )
    # We can have 123000.
            if hundredPart == SPECIAL_NUMBERS_NAMES['0']:
                hundredPart = ''
            else:
                hundredPart = ' ' + hundredPart
     
            thousandPart = printer( number = number[:-3],
                                    checkValidity = False )
     
    # We can have 000123 because of the recursive calls.
            if thousandPart == SPECIAL_NUMBERS_NAMES['0']:
                thousandPart = ''
            elif thousandPart == SPECIAL_NUMBERS_NAMES['1']:
                thousandPart = ten_powers_names[3]
            else:
    # Gramatical french boring rules for 'cent' like in 'quatre-cent mille'.
                thousandPart = boringFrenchGrammaticalRuleForCENT(thousandPart) + ' ' + ten_powers_names[3]
     
            return thousandPart + hundredPart
     
    # We have a number between 10^6 and 10^Pmax-1.
    #
    # For example with the convention "everyday",
    # we have Pmax = 9 and a number like
    #        123 456 789
    # must be treated as
    #        123       ---> Numbers of 10^6
    #        345678    ---> Lower than 10^6
    #
    # With the convention "chuquet",
    # we have Pmax = 60 and a number like
    #        123 456 789 012 345 678
    # must be treated as
    #        123456 ---> Numbers of 10^12
    #        789012 ---> Numbers of 10^6
    #        345678 ---> Lower than 10^6
    #
    # With the convention "rowlett",
    # we have Pmax = 90 and a number like
    #        123 456 789 012 345 678
    # must be treated as
    #        123    ---> Numbers of 10^15
    #        456    ---> Numbers of 10^12
    #        789    ---> Numbers of 10^9
    #        012    ---> Numbers of 10^6
    #        345678 ---> Lower than 10^6
        the_powers = THE_POWERS[convention]
        max_power = MAX_POWER[convention]
        len_number = len(number)
     
        if len_number <= max_power:
            answer = printer( number = number[-the_powers[1]:],
                              checkValidity = False )
    # We can have ...000.
            if answer == SPECIAL_NUMBERS_NAMES['0']:
                answer = ''
     
            for i in range(1, len(the_powers) - 1):
                if the_powers[i] > len_number:
                    break
     
                numberOfIntermediatePart = printer( number = number[-the_powers[i+1]:-the_powers[i]],
                                                    checkValidity = False )
    # We can have ...000...
                if numberOfIntermediatePart not in [SPECIAL_NUMBERS_NAMES['0'], '']:
    # Gramatical french boring rules for 'cent' like in 'quatre-cent millions'.
                    numberOfIntermediatePart = boringFrenchGrammaticalRuleForCENT(numberOfIntermediatePart)
     
                    if numberOfIntermediatePart == SPECIAL_NUMBERS_NAMES['1']:
                        numberOfIntermediatePart += ' ' + ten_powers_names[the_powers[i]]
                    else:
                        numberOfIntermediatePart += ' ' + ten_powers_names[the_powers[i]] + 's'
     
                    if answer:
                        answer =  numberOfIntermediatePart + ' ' + answer
                    else:
                        answer =  numberOfIntermediatePart
     
            return answer
     
     
    # We have a number bigger or equal to 10^Pmax.
    #
    # For example with the convention "everyday",
    # we have Pmax = 9 and a number like
    #        123 456789012 345678901 234567890
    # must be treated as
    #        123       ---> Numbers of 10^9 of 10^9 of 10^9
    #        456789012 ---> Numbers of 10^9 of 10^9
    #        345678901 ---> Numbers of 10^9
    #        234567890 ---> Lower than 10^9
        theBiggerName = THE_BIGGER_NAME[convention]
        currentBigPartName = ''
     
        answer = printer( number = number[-max_power:],
                          checkValidity = False )
        number = number[:-max_power]
    # We can have ...000.
        if answer == SPECIAL_NUMBERS_NAMES['0']:
            answer = ''
     
        while(number):
            numberOfIntermediatePart = printer( number = number[-max_power:],
                                                checkValidity = False )
            number = number[:-max_power]
     
    # We can have ...000...
            if numberOfIntermediatePart not in [SPECIAL_NUMBERS_NAMES['0'], '']:
    # Gramatical french boring rules for 'cent' like in 'quatre-cent millions'.
                numberOfIntermediatePart = boringFrenchGrammaticalRuleForCENT(numberOfIntermediatePart)
     
                if numberOfIntermediatePart == SPECIAL_NUMBERS_NAMES['1']:
                    numberOfIntermediatePart += ' ' + theBiggerName + currentBigPartName
    # We have to take care of case like "un million de milliards" = 10^12
                else:
                    for onePower in ten_powers_names:
                        if onePower != 3:
                            nameToTest = ten_powers_names[onePower]
                            l = len(nameToTest)
     
                            if numberOfIntermediatePart[-l:] == nameToTest or \
                            numberOfIntermediatePart[-l-1:] == nameToTest + 's':
                                numberOfIntermediatePart += ' de'
                                break
     
                    numberOfIntermediatePart += ' ' + theBiggerName + 's' + currentBigPartName
     
                if answer:
                    answer =  numberOfIntermediatePart + ' ' + answer
                else:
                    answer =  numberOfIntermediatePart
     
            currentBigPartName += ' de ' + theBiggerName + 's'
     
        return answer
     
     
    ###############
    ###############
    ##           ##
    ## FOR TESTS ##
    ##           ##
    ###############
    ###############
     
    if __name__ == '__main__':
        myConvention ="everyday"
        myConvention ="rowlett"
        myConvention ="chuquet"
     
        mytenPowerPrecision = 0
    #    mytenPowerPrecision = 3
     
        onlyTheOrderOfMagnitude = False
    #    onlyTheOrderOfMagnitude = True
     
        randomTest = True
        randomTest = False
        nMin = 0
        nMax = 10**18-1
        nbOfTest = 5
     
        test = [
            4,
            400, 5000, 600000,
            107,
            80, 1080,
            91, 71,
            184, 1840, 18400, 181000,
            400567,
            "1 200 000 567",
            "123 456 789",
            "123 456 789 012 345",
            10**6, 134*10**6, 10**9,
     
    #### mille millards
            "1000" + '0'*9,
    #### un million de millards
            "1" + '0'*6 + '0'*9,
    #### deux millions de millards
            "2" + '0'*6 + '0'*9,
    #### sept milliards de milliards de milliards
            "7" + '0'*(9*4),
               ]
     
        if randomTest:
            import random
            nMax += 1
            test = [random.randint(nMin, nMax) for x in range(nbOfTest)]
     
        for oneNumber in test:
            print(str(oneNumber))
     
            oneNumber = str(oneNumber).strip().replace(' ', '')
     
            if onlyTheOrderOfMagnitude:
                oneNumber = orderMagnitude(number = oneNumber)
                print('\torder of magnitude : ' + str(oneNumber))
     
            elif mytenPowerPrecision:
                oneNumber = floor(number = oneNumber,
                                  tenPowerPrecision = mytenPowerPrecision)
                print('\tfloor : ' + str(oneNumber))
     
            print('\t' + printer( number = oneNumber,
                                  convention = myConvention ))

  18. #18
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 751
    Points
    1 751
    Par défaut
    Citation Envoyé par eyquem Voir le message
    Cependant l’écriture donne une impression de rédaction pressée car il y a des choses balluches.
    Effectivement.

    Citation Envoyé par eyquem Voir le message
    Ce qui l’est moins, c’est l’algorithme, qui me paraît traînant par absence de vouloir toucher les objectifs en portant le fleuret au plus direct.
    Le comportement est je pense plus générique maintenant.

    Citation Envoyé par eyquem Voir le message
    La solution qui me plaît à moi, c’est de rajouter dans SPECIAL_NUMBERS_NAMES, ou un autre dictionnaire , des données qui vont simplifier la vie.
    Merci pour cette remarque TRES constructive.

    Citation Envoyé par eyquem Voir le message
    C’est à mon avis plus avantageux aussi bien au niveau de la lisibilité du code que de sa rapidité d’exécution.
    Je confirme...

    Citation Envoyé par eyquem Voir le message
    je ne vois pas la motivation à encombrer le code avec des checkValidity = False dans tous les appels de fonctions récursifs et de n’avoir la définition implicite de ce paramètre que dans le premier appel de printer() sur un nombre.
    J’ai donc fait l’inverse. Si c’est une mauvaise idée, merci de m’indiquer pourquoi.
    Je pense le contraire car je me place du côté de quelqu'un qui utiliserait mon code.

Discussions similaires

  1. [CR 8] Ecrire un nombre en toute lettre
    Par guandal dans le forum SAP Crystal Reports
    Réponses: 3
    Dernier message: 26/06/2009, 11h01
  2. Ecrire des chiffres en toutes lettres
    Par ali_Imouzzer dans le forum VB 6 et antérieur
    Réponses: 1
    Dernier message: 21/02/2007, 18h44
  3. Réponses: 9
    Dernier message: 07/11/2006, 14h12
  4. Réponses: 5
    Dernier message: 11/09/2006, 11h09
  5. [sql]convertion d'un nombre en toute lettre
    Par imedg dans le forum Oracle
    Réponses: 102
    Dernier message: 31/03/2006, 11h05

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo