• 数字与中文大写数字互转(5千万亿亿亿亿以上的数字也支持转换)


    来源是看到知乎上一个奇葩问题:

    image-20220807203558599

    任意大小的数字字符串转中文

    其实10万以内的整数转换大写也太简单,今天我们要实现的是任意大小的数字字符串转大写。

    首先我们要保证4位数以内的转换函数,能够处理各种含"0"的情况,代码:

    import re
    
    ch_num = '零一二三四五六七八九'
    units = ' 十百千'
    
    
    def num2Chinese1(num_str):
        "转换9999以内的数字到大写"
        result = [ch_num[int(i)]+unit for i,
                  unit in zip(reversed(num_str), units)]
        result = "".join(reversed(result))
        result = re.sub("(?:零[十百千])+", "零", result)
        result = re.sub("零+", "零", result)
        result = result.rstrip()
        if result != "零":
            result = result.rstrip("零")
        return result
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    然后生成测试用例并测试:

    import random
    import itertools
    
    num_t = "123456789"
    cases = []
    for length in range(1, 5):
        nums = random.sample(num_t, length)
        cases.append("".join(nums))
        for n in range(1, length+1):
            # n个0
            for poss in itertools.combinations(range(length), n):
                num = nums.copy()
                for pos in poss:
                    num[length-pos-1] = "0"
                num = "".join(num)
                cases.append(num)
    for i in cases:
        print(i, num2Chinese1(str(i)))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    6 六
    0 零
    54 五十四
    50 五十
    04 零四
    00 零
    831 八百三十一
    830 八百三十
    801 八百零一
    031 零三十一
    800 八百
    030 零三十
    001 零一
    000 零
    6895 六千八百九十五
    6890 六千八百九十
    6805 六千八百零五
    6095 六千零九十五
    0895 零八百九十五
    6800 六千八百
    6090 六千零九十
    0890 零八百九十
    6005 六千零五
    0805 零八百零五
    0095 零九十五
    6000 六千
    0800 零八百
    0090 零九十
    0005 零五
    0000 零
    
    • 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

    保证了四位数的处理函数没有问题后,然后就可以开始编写能够支持任意位数的处理函数,并将转换函数升级到支持小数。

    import math
    import re
    
    ch_num = '零一二三四五六七八九'
    units = ' 十百千'
    
    def num2Chinese1(num_str):
        "转换9999以内的数字到大写"
        result = [ch_num[int(i)]+unit for i,
                  unit in zip(reversed(num_str), units)]
        result = "".join(reversed(result))
        result = re.sub("(?:零[十百千])+", "零", result)
        result = re.sub("零+", "零", result)
        result = result.rstrip()
        if result != "零":
            result = result.rstrip("零")
        return result
    
    
    def num2Chinese2(num_str):
        if len(num_str) <= 4:
            result = num2Chinese1(num_str[-4:])
        else:
            result = num2Chinese1(num_str[:-4]) + "万" + num2Chinese1(num_str[-4:])
        return result.replace("零万", "零")
    
    
    def num2Chinese3(num_str):
        num_str = num_str.lstrip("0")
        if not num_str:
            return "零"
        if len(num_str) <= 4:
            return num2Chinese1(num_str)
        e_num = math.ceil(len(num_str)/8)
        result = [num2Chinese2(num_str[-8:])]
        for e_i in range(1, e_num):
            result.append(num2Chinese2(num_str[-8-e_i*8:-e_i*8])+"亿"*e_i+" ")
        result = "".join(reversed(result))
        result = re.sub(" (?:零+亿+ )+", " 零", result)
        result = re.sub("零(亿+)", r"\1", result)
        result = re.sub("零+", "零", result)
        return result.strip("零")
    
    
    def num2Chinese(num_str):
        num_str = num_str.replace(" ", "").rstrip(".")
        if not re.fullmatch("\d+(?:\.\d+)?", num_str):
            raise Exception("不是一个数字字符串")
        if num_str.find(".") != -1:
            num_str1, num_str2 = num_str.split(".", 1)
            return num2Chinese3(num_str1)+" 点 "+"".join(ch_num[int(c)] for c in num_str2.rstrip("0"))
        return num2Chinese3(num_str)
    
    • 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

    下面我们生成千万亿级别的各种具备代表性的测试数据进行测试:

    nums_t = random.choices("123456789", k=16)
    num = "".join(nums_t)
    print(num, num2Chinese(num))
    for zero_len in range(4, 15, 3):
        for s_pos in range(17-zero_len):
            nums = nums_t.copy()
            nums[s_pos:s_pos+zero_len] = "0"*zero_len
            num = "".join(nums)
            print(num, num2Chinese3(num))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    9681852243533934 九千六百八十一万八千五百二十二亿 四千三百五十三万三千九百三十四
    0000852243533934 八千五百二十二亿 四千三百五十三万三千九百三十四
    9000052243533934 九千万零五百二十二亿 四千三百五十三万三千九百三十四
    9600002243533934 九千六百万零二十二亿 四千三百五十三万三千九百三十四
    9680000243533934 九千六百八十万零二亿 四千三百五十三万三千九百三十四
    9681000043533934 九千六百八十一万亿 四千三百五十三万三千九百三十四
    9681800003533934 九千六百八十一万八千亿 零三百五十三万三千九百三十四
    9681850000533934 九千六百八十一万八千五百亿 零五十三万三千九百三十四
    9681852000033934 九千六百八十一万八千五百二十亿 零三万三千九百三十四
    9681852200003934 九千六百八十一万八千五百二十二亿 零三千九百三十四
    9681852240000934 九千六百八十一万八千五百二十二亿 四千万零九百三十四
    9681852243000034 九千六百八十一万八千五百二十二亿 四千三百万零三十四
    9681852243500004 九千六百八十一万八千五百二十二亿 四千三百五十万零四
    9681852243530000 九千六百八十一万八千五百二十二亿 四千三百五十三万
    0000000243533934 二亿 四千三百五十三万三千九百三十四
    9000000043533934 九千万亿 四千三百五十三万三千九百三十四
    9600000003533934 九千六百万亿 零三百五十三万三千九百三十四
    9680000000533934 九千六百八十万亿 零五十三万三千九百三十四
    9681000000033934 九千六百八十一万亿 零三万三千九百三十四
    9681800000003934 九千六百八十一万八千亿 零三千九百三十四
    9681850000000934 九千六百八十一万八千五百亿 零九百三十四
    9681852000000034 九千六百八十一万八千五百二十亿 零三十四
    9681852200000004 九千六百八十一万八千五百二十二亿 零四
    9681852240000000 九千六百八十一万八千五百二十二亿 四千万
    0000000000533934 五十三万三千九百三十四
    9000000000033934 九千万亿 零三万三千九百三十四
    9600000000003934 九千六百万亿 零三千九百三十四
    9680000000000934 九千六百八十万亿 零九百三十四
    9681000000000034 九千六百八十一万亿 零三十四
    9681800000000004 九千六百八十一万八千亿 零四
    9681850000000000 九千六百八十一万八千五百亿 
    0000000000000934 九百三十四
    9000000000000034 九千万亿 零三十四
    9600000000000004 九千六百万亿 零四
    9680000000000000 九千六百八十万亿 
    0000000000000000 零
    
    • 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

    目测已经覆盖所有基本情况,下面我们测试一个带小数的超大数字:

    num2Chinese("9506335 25566321 00000000 18945760 00424584 54444444 00000000 00000000 00000000 44487878 00000000 44487878.00980213")
    
    • 1
    '九百五十万六千三百三十五亿亿亿亿亿亿亿亿亿亿亿 二千五百五十六万六千三百二十一亿亿亿亿亿亿亿亿亿亿 零一千八百九十四万五千七百六十亿亿亿亿亿亿亿亿 零四十二万四千五百八十四亿亿亿亿亿亿亿 五千四百四十四万四千四百四十四亿亿亿亿亿亿 零四千四百四十八万七千八百七十八亿亿 零四千四百四十八万七千八百七十八 点 零零九八零二一三'
    
    • 1

    测试一个python所支持的最大整数:

    num2Chinese("9223372036854775807")
    
    • 1
    '九百二十二亿亿 三千三百七十二万零三百六十八亿 五千四百七十七万五千八百零七'
    
    • 1

    可以初步判断能够正确转换。

    中文数字转数字

    上面我们连如此复杂的数字转中文都实现了,中文转数字将会变得非常简单,完整代码如下:

    import re
    
    num_ch = dict(zip('零一二三四五六七八九', map(str, range(10))))
    num_units = dict(zip('十百千', range(1, 4)))
    num_units[""] = 0
    
    
    def Chinese2Num1(ch_num_str):
        result = ["0"]*4
        for num, unit in re.findall("([^零十百千])([十百千]|$)", ch_num_str):
            result[num_units[unit]] = num_ch[num]
        result.reverse()
        return "".join(result)
    
    
    def Chinese2Num2(ch_num_str):
        if ch_num_str.find("万") != -1:
            ch_num_str1, ch_num_str2 = map(Chinese2Num1, ch_num_str.split("万", 1))
            return ch_num_str1 + ch_num_str2
        return Chinese2Num1(ch_num_str).zfill(8)
    
    
    def Chinese2Num3(ch_num_str):
        data = re.findall("[ 零]*([^亿]+)(亿+|$)", ch_num_str)
        result = ["0"*8]*(len(data[0][1])+1)
        for Chinese_str, e_str in data:
            result[len(e_str)] = Chinese2Num2(Chinese_str)
        result.reverse()
        return "".join(result).lstrip("0")
    
    
    def Chinese2Num(ch_num_str):
        ch_num_str = ch_num_str.replace(" ", "").rstrip("点零")
        if ch_num_str == "":
            return "0"
        if not re.fullmatch("[零一二三四五六七八九十百千万亿]+(?:点[零一二三四五六七八九十百千万亿]+)?", ch_num_str):
            raise Exception(f"{ch_num_str}存在非法字符,无法转换")
        if ch_num_str.find("点") != -1:
            ch_num_str1, ch_num_str2 = ch_num_str.split("点", 1)
            return Chinese2Num3(ch_num_str1)+"."+"".join(num_ch[c] for c in ch_num_str2).rstrip("0")
        return Chinese2Num3(ch_num_str)
    
    • 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

    测试一下:

    Chinese2Num('九百五十万六千三百三十五亿亿亿亿亿亿亿亿亿亿亿 二千五百五十六万六千三百二十一亿亿亿亿亿亿亿亿亿亿 零一千八百九十四万五千七百六十亿亿亿亿亿亿亿亿 零四十二万四千五百八十四亿亿亿亿亿亿亿 五千四百四十四万四千四百四十四亿亿亿亿亿亿 零四千四百四十八万七千八百七十八亿亿 零二百六十五万三千零二十一 点 零零九八零二一三')
    
    • 1
    '95063352556632100000000189457600042458454444444000000000000000000000000444878780000000002653021.00980213'
    
    • 1

    测试

    接下面我们可以让两个转换函数互相转换,互相验证:

    for i in range(0, 720368547758, 900025):
        a = str(i)
        ch_num_str = num2Chinese(a)
        b = Chinese2Num(ch_num_str)
        if a != b:
            print(a, ch_num_str, b)
            break
    else:
        print("未发现转换错误!")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    经过半分钟的时间,已经初步互相验证完成,未发现转换失败的数据。

    彩蛋:windows文件名排序

    如何快速实现windows文件名的排序效果呢?示例代码:

    files = os.listdir()
    files.sort(key=lambda s: [(s, int(n)) for s, n in re.findall('(\D+)(\d+)', f'a{s}0')])
    
    • 1
    • 2

    要支持能够根据中文大写数字排序,也只需要先将大写数字都转换为普通数字再用上述方法排序即可。

  • 相关阅读:
    【MATLAB第80期】基于MATLAB的结构核岭回归SKRR多输入单输出回归预测及分类预测模型
    【Android 屏幕适配】屏幕适配通用解决方案 ③ ( 自定义组件解决方案 | 获取设备状态栏高度 | 获取设备屏幕数据 )
    Mybatis-Flex框架初体验
    数据结构--第九章--查找
    测试人进阶技能:单元测试报告应用指南
    【计算机毕业设计】小型OA系统设计与实现Springboot
    什么是无损检测设备?
    python
    基于SSM实现的儿童疫苗信息管理系统设计与实现毕业设计源码311930
    语法基础(函数)
  • 原文地址:https://blog.csdn.net/as604049322/article/details/126236369