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 :

convertir un nombre en Lettre en python


Sujet :

Python

  1. #1
    Membre à l'essai
    Femme Profil pro
    Étudiant
    Inscrit en
    Mai 2011
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Maroc

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Industrie

    Informations forums :
    Inscription : Mai 2011
    Messages : 7
    Points : 11
    Points
    11
    Par défaut convertir un nombre en Lettre en python
    Bonjour à tous,

    existe-t-il en python une fonction pour convertir un nombre en Lettre?

    je travail sur libreoffice et je veux ajouter la phrase suivante en bas de la facture : " arrêté la présente facture à la somme : Mille quatre cents quarante dirhams."

    aidez moi svp

    slts

  2. #2
    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 752
    Points
    1 752
    Par défaut
    Bonjour,
    j'ai fait un script pour les nombres entiers. Voici les fichiers avec un petit bonus pour passer des lettres aux chiffres.

    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
    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 = {}
     
    # 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
    407
    # -*- 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.
    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 ))
    wordsToInteger_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
    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
    #s!/usr/bin/env python
     
    # This code only works with Python 3.
     
    # CODE TO CLEAN AND SIMPLIFY !!!!!!
     
    import re       # For cleaning the spaces between digits.
    import difflib  # For finding closest number names to a mistaped word.
     
    import config_fr
     
    # We don't keep 70, 80 and 90 because we are going to transform "soixante dix" to "70"...
    # We need to do that so have to be more flexible.
    SPECIAL_NAMES_NUMBERS = dict([ (val, key) for (key, val) in config_fr.SPECIAL_NUMBERS_NAMES.items() \
                                   if key not in ['80', '90', '70'] ])
     
    NAMES_TEN_POWERS = {}
    THE_BIGGEST_NUMBER = {}
    THE_TEN_POWERS = {}
     
    for oneConvention in config_fr.TEN_POWERS_NAMES:
        NAMES_TEN_POWERS[oneConvention] = {}
        THE_TEN_POWERS[oneConvention] = ['100']
     
        for (key, val) in config_fr.TEN_POWERS_NAMES[oneConvention].items():
            NAMES_TEN_POWERS[oneConvention][val] = '1' + '0'*key
            THE_TEN_POWERS[oneConvention].append('1' + '0'*key)
     
    # We have to sort the powers from the smaller to the bigger.
        THE_TEN_POWERS[oneConvention] = sorted( THE_TEN_POWERS[oneConvention],
                                                key=(lambda x: len(x)) )
     
        THE_BIGGEST_NUMBER[oneConvention] = THE_TEN_POWERS[oneConvention][-1]
     
    # We  must import integerToWords_fr after config_fr because
    # SPECIAL_NUMBERS_NAMES is changed in integerToWords_fr.
    import integerToWords_fr # We need this to indicate errors...
     
     
    ###############
    # THE FUNCTIONS
     
    def wellFormattedTextWithDigits(text):
    # Source : http://www.developpez.net/forums/d867083/autres-langages/python-zope/general-python/nettoyer-espaces-regex
        return list( map(lambda x: x.replace(' ',''),re.findall('(\d[\d ]*|[^ \d-]+)',text)) )
     
     
    def printer(text, allowMistype = False, allowNumber = False, convention = "everyday"):
        """
        If sympaMode = True, then we try to find the closest word possible
        when mistyping are done. For example 'katr" will be seen as "quatre".
     
        **NOTE :** this functionnality is the work of the module 'difflib'.
        """
        if convention not in NAMES_TEN_POWERS:
            raise ValueError('convention = "' + str(convention) + '" is unknown.')
     
        text = text.lower()
     
        if allowNumber:
    # "3456 7890" must become "34567890".
            theWords = wellFormattedTextWithDigits(text)
        else:
            theWords = text.replace('-', ' ').split()
     
    # We verify that the names used are allowed.
        names_ten_powers = NAMES_TEN_POWERS[convention]
     
        for index, oneWord in enumerate(theWords):
    # "cents", "millions",... must be seen as "cent", "million",...
    #
    # WARNING ! "trois" must stay "trois"
            if oneWord[-1] == 's' and oneWord != 'trois':
                oneWord = oneWord[:-1]
                weCleanAnS = True # Only for error detected.
            else:
                weCleanAnS = False
     
    # WARNING ! "trente et un milliards de milliards"
    #           "deux millions de milliards"
            if oneWord in ['et', 'de']:
                pass
    # We convert each word to digit numbers.
            elif oneWord in SPECIAL_NAMES_NUMBERS:
                theWords[index] = SPECIAL_NAMES_NUMBERS[oneWord]
            elif oneWord in names_ten_powers:
                theWords[index] = names_ten_powers[oneWord]
            else:
                integerCleaned = integerToWords_fr.cleanInteger(oneWord)
     
                if integerCleaned:
                    if allowNumber:
                        theWords[index] = integerCleaned
                    else:
                        raise ValueError( 'The integer "{0}" must be written using words !.'.format(str(oneWord)) )
     
                elif allowMistype:
    # 'difflib' does all the job !
                    nearestWords = difflib.get_close_matches(oneWord, SPECIAL_NAMES_NUMBERS)
     
                    if len(nearestWords) == 1:
                        theWords[index] = SPECIAL_NAMES_NUMBERS[nearestWords[0]]
                    else:
                        if weCleanAnS:
                            oneWord += 's'
                        raise ValueError( '"{0}" does not look closer to a name numbers with the "{1}" convention.'.format(str(oneWord), convention) )
     
                else:
                    if weCleanAnS:
                        oneWord += 's'
                    raise ValueError( '"{0}" is not used to name numbers with the "{1}" convention.'.format(str(oneWord), convention) )
     
    # For the moment we have ['4', '20'] instead of '80'.
    # We have a similar problem with '90'.
    #
    # We have to treat that...
        i = 0
        lookFor = '4' in theWords
     
        while lookFor:
            i = theWords.index('4', i)
     
            if i < len(theWords) - 1:
                if theWords[i + 1] == '20':
                    theWords.pop(i + 1)
                    if i == len(theWords) - 1:
                        theWords[i] = '80'
                    elif theWords[i + 1] == '10':
                        theWords.pop(i + 1)
                        theWords[i] = '90'
                    else:
                        theWords[i] = '80'
     
            try:
                i = theWords.index('4', i + 1)
                lookFor = True
            except:
                lookFor = False
     
    # We look for a good use of the word "et".
        while('et' in theWords):
            i = theWords.index('et')
     
            if i == 0:
                raise ValueError('"et" can not be at the beggining of the name of the number.')
     
            if i == len(theWords) - 1:
                raise ValueError('"et" can not be at the end of the name of the number.')
     
            beforeET = theWords[i-1]
            afterET = theWords[i+1]
     
            oneError = False
     
            if afterET == '1':
                if not beforeET in ['20', '30', '40', '50', '60', '80']:
                    oneError = True
                else:
                    theWords[i-1] = theWords[i-1][0] + '1'
     
            elif afterET == '11':
                if not beforeET == '60':
                    oneError = True
                else:
                    theWords[i-1] = str( int(theWords[i-1][0]) + 1 ) + '1'
     
            else:
                oneError = True
     
            if oneError:
                beforeET = integerToWords_fr.printer( number = beforeET,
                                                      convention = convention )
                afterET = integerToWords_fr.printer( number = afterET,
                                                     convention = convention )
                raise ValueError( '"{0}" can not be used to name numbers with the "{1}" convention.'.format(str(beforeET +'-et-' + afterET), convention) )
     
            theWords.pop(i)
            theWords.pop(i)
     
    # We look for a good use of the word "de".
        the_biggest_number = THE_BIGGEST_NUMBER[convention]
        the_ten_powers = THE_TEN_POWERS[convention]
     
        while('de' in theWords):
            i = theWords.index('de')
     
            if i == 0:
                raise ValueError('"de" can not be at the beggining of the name of the number.')
     
            if i == len(theWords) - 1:
                raise ValueError('"de" can not be at the end of the name of the number.')
     
            beforeDE = theWords[i - 1]
            afterDE = theWords[i + 1]
     
            if (beforeDE not in the_ten_powers and beforeDE != '*') or afterDE != the_biggest_number:
                beforeDE = integerToWords_fr.printer( number = beforeDE,
                                                      convention = convention )
                afterDE = integerToWords_fr.printer( number = afterDE,
                                                     convention = convention )
                raise ValueError( '"{0}" can not be used to name numbers with the "{1}" convention.'.format(str(beforeDE +' de ' + afterDE.replace('un ', '')), convention) )
     
    # '*' means '.... * 10^max * ...'
            if beforeDE == the_biggest_number:
                theWords[i] = '*'
                theWords.pop(i + 1)
            else:
                theWords.pop(i)
     
    # We build the two digits numbers like "30-5  == > 35", "80-15  == > 95"...
        i = 0
        while i < len(theWords):
            addOne = True
     
    # ERRORS TO FIND : "4-30" or "5-6" !!!!
            if len(theWords[i]) <= 2 and i > 0 and theWords[i] != '*':
                if len(theWords[i - 1]) == 1 and theWords[i-1] != '*':
                    theWords[i - 1] = integerToWords_fr.printer( number = theWords[i - 1],
                                                                 convention = convention )
                    theWords[i] = integerToWords_fr.printer( number = theWords[i],
                                                             convention = convention )
                    raise ValueError( '"{0}" can not be used to name numbers with the "{1}" convention.'.format(str(theWords[i - 1] + ' ' + theWords[i]), convention) )
     
            if len(theWords[i]) == 2:
                if i < len(theWords) - 1:
                    if theWords[i + 1] != '*':
    # "30-5  == > 35"
                        if len(theWords[i + 1]) == 1:
                            if theWords[i][1] != '0':
                                theWords[i] = integerToWords_fr.printer( number = theWords[i],
                                                                         convention = convention )
                                theWords[i + 1] = integerToWords_fr.printer( number = theWords[i+1],
                                                                             convention = convention )
                                raise ValueError( '"{0}" can not be used to name numbers with the "{1}" convention.'.format(str(theWords[i] + ' ' + theWords[i + 1]), convention) )
     
                            theWords[i] = theWords[i][0] + theWords[i + 1]
                            theWords.pop(i + 1)
                            addOne = False
    # "80-15  == > 95"
                        elif len(theWords[i + 1]) == 2:
                            if theWords[i] not in ['60', '80'] or theWords[i + 1][0] != '1':
                                theWords[i] = integerToWords_fr.printer( number = theWords[i],
                                                                         convention = convention )
                                theWords[i + 1] = integerToWords_fr.printer( number = theWords[i+1],
                                                                             convention = convention )
                                raise ValueError( '"{0}" can not be used to name numbers with the "{1}" convention.'.format(str(theWords[i] + ' ' + theWords[i + 1]), convention) )
     
                            theWords[i] = str(int(theWords[i][0]) + 1) + theWords[i + 1][1]
                            theWords.pop(i + 1)
                            addOne = False
     
    # ERRORS TO FIND : "30-100" !!!!
                        elif theWords[i + 1] == '100':
                            theWords[i] = integerToWords_fr.printer( number = theWords[i],
                                                                     convention = convention )
                            theWords[i + 1] = integerToWords_fr.printer( number = theWords[i+1],
                                                                         convention = convention )
                            raise ValueError( '"{0}" can not be used to name numbers with the "{1}" convention.'.format(str(theWords[i] + ' ' + theWords[i + 1]), convention) )
     
            if addOne:
                i += 1
     
    # We build the mutiples of 100, 1 000, 1 000 000 and 10^p for p > 6 .
    #
    # Suppose that we have theWords equal to
    #        ['100', '34', '1000', '7', '100', '95', '1000000000', '*']
    # The following procedures will produce first
    #        ['134', '1000', '795', '1000000000', '*']
    # and then
    #        ['134 795', '1000000000', '*']
    # We do the analyse from the smaller power of ten to the bigger one.
    #
    # The treatment of [..., '*'] will be done after.
        for currentTenPower  in the_ten_powers:
    # The forbidden writing "trente cents" have been treated before.
            nbOfDigitsAllowed = len(currentTenPower) - 1
     
    # SPECIAL RULES  ===> ?00            "un cent"      NO !
    #                     ??? 000        "un mille"     NO !
    #                     ??? 000 000    "un million"   YES !
    #                     The same is true for the other "big" powers of ten.
            if currentTenPower in ['100', '1000']:
                oneAllowed = False
            else:
                oneAllowed = True
     
            i = 0
            while i < len(theWords):
                addOne = True
     
                if theWords[i] == currentTenPower:
                    if i < len(theWords) - 1:
                        if theWords[i + 1][0] != '*':
    # "?00-45  == > ?45"
                            if len(theWords[i + 1]) <= nbOfDigitsAllowed:
                                theWords[i + 1] = '0'*(nbOfDigitsAllowed - len(theWords[i + 1])) + theWords[i + 1]
                                theWords[i] = theWords[i][0] + theWords[i + 1][-nbOfDigitsAllowed:]
                                theWords.pop(i + 1)
                                addOne = False
     
    # "5-1??  == > 5??"
                    if i > 0:
                        if theWords[i - 1][0] != '*':
                            if len(theWords[i - 1]) <= nbOfDigitsAllowed:
                                theWords[i - 1] = theWords[i - 1] + theWords[i][1:]
                                theWords.pop(i)
                                addOne = False
     
                if addOne:
                    i += 1
     
    # A single word ?
        if len(theWords) == 1:
            return theWords[0]
     
    # ERRORS TO FIND : "3456-1000" , "3456-6789" ,...
        for i in range(len(theWords)-1):
            if theWords[i + 1] != '*' \
            and theWords[i + 1] != '*' \
            and len(theWords[i]) >= len(theWords[i + 1]):
                theWords[i] = integerToWords_fr.printer( number = theWords[i],
                                                         convention = convention )
                theWords[i + 1] = integerToWords_fr.printer( number = theWords[i+1],
                                                             convention = convention )
                raise ValueError( '"{0}" can not be used to name numbers with the "{1}" convention.'.format(str(theWords[i] + ' ' + theWords[i + 1]), convention) )
     
    # To finish we have to treat things like "... milliars de milliards..."
    #
    # '*' means '.... * 10^max * ...'
    # For example, we have :
    #    "un milliard de milliards"
    #            ---> ['1000000000', '*']
    #    "deux milliards de milliards deux cents"
    #            ---> ['2000000000', '*', '200']
        answer = ''
        maxiNbOfZeros = len(the_biggest_number) - 1
        i = len(theWords) - 1
     
        while i >= 0:
            if theWords[i] == '*':
                nbOfDE = 2
                i -= 1
     
                while theWords[i] == '*':
                    nbOfDE += 1
                    i -= 1
    # "deux cent trois milliards de milliards quatre-vingt"
    #        ---> ['203000000000', '*', '80']
                theWords[i] = theWords[i][:-maxiNbOfZeros]
    # We have then : ['203', '000000080']
     
                if len(answer) > maxiNbOfZeros*nbOfDE:
                    answer = integerToWords_fr.printer( number = answer,
                                                        convention = convention )
                    the_biggest_number = integerToWords_fr.printer( number = the_biggest_number,
                                                                    convention = convention ).replace('un', '')
                    raise ValueError( 'You misuse "... {0}" before "{1}".'.format('de' + the_biggest_number, answer) )
                answer = '0'*(maxiNbOfZeros*nbOfDE - len(answer)) + answer
     
            else:
                answer = theWords[i] + answer
                i -= 1
     
        return answer
     
     
    ###############
    ###############
    ##           ##
    ## FOR TESTS ##
    ##           ##
    ###############
    ###############
     
    if __name__ == '__main__':
        myConvention ="everyday"
    #    myConvention ="rowlett"
    #    myConvention ="chuquet"
     
        weAllowMistype = False
        weAllowMistype = True
     
        weAllowNumber = False
        weAllowNumber = True
     
        randomTest = True
        randomTest = False
        nMin = 0
        nMax = 10**50
        nbOfTest = 100
     
        test = [
            'quatre six',
            'quatre  cinquante',
            '3456      mille',
            'mille 7890',
            '3456 sept mille 890',
            '3456      7890',
            'sans douse',
            'troiiii cen douse',
            'cinquante six',
            '30et un mille quatre cents       quatre-vingt-onze millions soixante-et-onze',
            'deux 100 mille trois',
            'trois millions de milliards',
            'trente-quatre mille sept cents quatre-vingt-quinze milliards de milliards deux cents milliards',
            'sept milliards de milliards de milliards deux cents milliards',
            'sept milliards de milliards de milliards '
               ]
     
        if randomTest:
            import random
            nMax += 1
     
            for x in range(nbOfTest):
                print()
     
                oneNumber = str( random.randint(nMin, nMax) )
                print( 'Test No {0} : {1}'.format(str(x+1), oneNumber) )
     
                words = integerToWords_fr.printer( number = oneNumber,
                                                   convention = myConvention )
                print(words)
                print()
     
                if oneNumber != printer( text = words,
                                         allowMistype = weAllowMistype,
                                         allowNumber = weAllowNumber,
                                         convention = myConvention ):
                    print( printer( text = words,
                                    allowMistype = weAllowMistype,
                                    allowNumber = weAllowNumber,
                                    convention = myConvention ) )
                    raise Exception('PB WITH : ' + '\n\t' + oneNumber + '\n\t' + printer(words) + '\n\t' + words)
            print()
            print('END OF THE RANDOM TESTS.')
     
        else:
            for oneNumber in test:
                print()
                print(str(oneNumber))
                try:
                    print('\t' + printer( text = oneNumber,
                                          allowMistype = weAllowMistype,
                                          allowNumber = weAllowNumber,
                                          convention = myConvention ))
                except:
                     print('\tUNCORRECT NUMBER !')

Discussions similaires

  1. convertir un nombre en lettre
    Par moha1984 dans le forum C#
    Réponses: 8
    Dernier message: 16/09/2023, 00h21
  2. Convertir un nombre en lettre
    Par hpalpha dans le forum Contribuez
    Réponses: 2
    Dernier message: 22/12/2017, 09h07
  3. [AC-2000] comment convertir un nombre en lettre
    Par finetos dans le forum Access
    Réponses: 2
    Dernier message: 19/11/2009, 19h58
  4. Convertir un nombre en lettres ?
    Par narmataru dans le forum API standards et tierces
    Réponses: 2
    Dernier message: 12/05/2009, 18h18
  5. Convertir un nombre en lettre et en anglais
    Par lessoy dans le forum VB 6 et antérieur
    Réponses: 5
    Dernier message: 02/03/2007, 18h15

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