zoukankan      html  css  js  c++  java
  • 如何将数字转换成口语中的文本串

    概述

    今天突发奇想, 写一个将数字转换成中文字符串的函数. 并不是将 1234 转成 '1234' , 而是将 1234 转成 '一千二百三十四'.

    本来以为很简单, 写下来之后发现还是有些坑的.

    尝试

    因为我是在写完最终版本, 回过头来整理的这篇文章, 所以中间很多尝试的步骤会有所遗漏. 以下简单整理一下. 如果不想看, 可以直接拉到最后, 看最终的成品.

    第一次尝试

    在写之前, 首先要寻找中文说话的规律嘛.

    1. 数字的念法: 零一二三四五六七八九
    2. 每一位都有一个对应的权重: 个十百千万

    所以我的初步想法是, 将数字的每一位都转成中文然后拼上对应的权重, so easy. 以下为 Python 实现:

    # 数字中文
    DIGIT_STR_LIST = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九']
    # 权重中文
    WIGHT_STR_LIST = ['', '十', '百', '千', '万', '十万', '百万', '千万', '亿']
    
    def num_to_str(num):
        # 保存每一位的内容
        result_list = []
        # 遍历数字的每一位, 将数组转列表并倒序遍历
        for wight, digit in enumerate(reversed(list(str(num)))):
            digit = int(digit)
            digit_str = DIGIT_STR_LIST[digit] if digit < len(DIGIT_STR_LIST) else ''
            wight_str = WIGHT_STR_LIST[wight] if wight < len(WIGHT_STR_LIST) else ''
            # 结果拼接
            result_list.append(digit_str + wight_str)
        # 将结果倒序拼接
        result_list.reverse()
        return "".join(result_list)
    

    OK, 写的很流畅, 也很简单, 尝试一下.

    • 传参: 1234 , 输出: 一千二百三十四 . 很完美.
    • 五位数试一下: 54321. 输出: 五万四千三百二十一. nice
    • 六位数试一下: 654321 . 输出: 六十万五万四千三百二十一. ???

    有问题. 这里问题很明显了, 我将权重直接拼到了每一位的后边, 而十万直接拼上去明显有问题. 正解应该是六十五万四千三百二十一.

    到这里, 毫无疑问, 一开始思路就错了, 需要重新改变一下思路了.

    第二次尝试

    对于654321这个数字.

    十万位6没有将十万直接拼到后边, 而是和万位5连起来, 一起组成了六十五万. 再多一个数字呢? 7654321, 就应该是七百六十五万. 我貌似发现规律了, 把数字切分为四个一组就可以了.

    再看一下位数多一点的数字: 1-2345-6789. 中文是: 一亿-二千三百四十五万-六千七百八十九 嗯, 和我预想得一毛一样. 大概懂了, 着手改进一下:

    # 数字中文
    DIGIT_STR_LIST = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九']
    # 权重中文
    WIGHT_STR_LIST = ['', '十', '百', '千']
    # 分组后对应的中文
    GROUP_STR_LIST = ['', '万', '亿', '兆']
    
    
    def thousand_list_num_to_str(num_list: list) -> str:
        """
        将4位数字转成字符串
        :param num_list: 数字列表, 长度不超过4. 索引和数字对应为: 个十百千
        :return:
        """
        # 保存每一位的内容
        result_list = []
        # 遍历数字的每一位, 将数组转列表并倒序遍历
        for wight, digit in enumerate(num_list):
            digit = int(digit)
            digit_str = DIGIT_STR_LIST[digit] if digit < len(DIGIT_STR_LIST) else ''
            wight_str = WIGHT_STR_LIST[wight] if wight < len(WIGHT_STR_LIST) else ''
            # 结果拼接
            result_list.append(digit_str + wight_str)
        # 将结果倒序拼接
        result_list.reverse()
        return "".join(result_list)
    
    
    def num_to_str(num : int) -> str:
        """
        将数组装成中文
        :param num:
        :return:
        """
        # 将数字切割为每四个一组, 分别进行处理
        num_list = list(str(num))
        # 这里为了处理长度不是4整数倍的情况, 提前反转.
        num_list.reverse()
        group_num_list = [num_list[i:i+4] for i in range(0, len(num_list), 4)]
        result_list = []
        # 遍历每一组, 并产生对应中文输出
        for group, num_list in enumerate(group_num_list):
            this_num_str = thousand_list_num_to_str(num_list)
            group_str = GROUP_STR_LIST[group] if group < len(GROUP_STR_LIST) else ''
            result_list.append(this_num_str + group_str)
        result_list.reverse()
        return ''.join(result_list)
    

    OK! 现在已经可以应对刚才的情况了. 试一下:

    • 654321 -> 六十五万四千三百二十一
    • 321 -> 三百二十一
    • 120 -> 一百二十
    • 10101010 -> 一千百一十万一千百一十 纳尼????
    • 1000 -> 一千百一十 纳尼???

    很明显, 问题出在thousand_list_num_to_str 这个函数.

    四位数的时候, 0应该是要跳过的.

    第三次尝试

    我们对thousand_list_num_to_str函数进行简单的改进, 遇到零的时候直接跳过, 不进行处理. 改进后如下(只展示了部分改动的地方):

    DIGIT_STR_LIST = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
    
    
    def thousand_list_num_to_str(num_list: list) -> str:
        """
        将4位数字转成字符串
        :param num_list: 数字列表, 长度不超过4. 索引和数字对应为: 个十百千
        :return:
        """
        # 保存每一位的内容
        result_list = []
        # 遍历数字的每一位, 将数组转列表并倒序遍历
        for wight, digit in enumerate(num_list):
            digit = int(digit)
            # 0无输出
            if digit is 0:
                continue
            digit_str = DIGIT_STR_LIST[digit] if digit < len(DIGIT_STR_LIST) else ''
            wight_str = WIGHT_STR_LIST[wight] if wight < len(WIGHT_STR_LIST) else ''
            # 结果拼接
            result_list.append(digit_str + wight_str)
        # 将结果倒序拼接
        result_list.reverse()
        return "".join(result_list)
        
    

    OK, 再次尝试.

    • 10101010 -> 一千一十万一千一十 nice!
    • 100 -> 一百 nice!
    • 1210 -> 一千二百一十
    • 1201 -> 一千二百一 纳尼??

    这里按照思维, 应该是输出一千二百零一才对. 继续对thousand_list_num_to_str函数进行加工.

    第四次尝试

    这里thousand_list_num_to_str函数要将零输出, 但是要考虑连续为零的情况(前边的100). 改动后代码如下:

    def thousand_list_num_to_str(num_list: list) -> str:
        """
        将4位数字转成字符串
        :param num_list: 数字列表, 长度不超过4. 索引和数字对应为: 个十百千
        :return:
        """
        # 保存每一位的内容
        result_list = []
        # 遍历数字的每一位, 将数组转列表并倒序遍历
        for wight, digit in enumerate(num_list):
            digit = int(digit)
            if digit is 0:
                # 个位的0无输出
                if wight is 0:
                    continue
                # 连续0无输出
                elif int(num_list[wight-1]) is 0:
                    continue
                # 直接拼零
                result_list.append(ZERO_STR)
                continue
            digit_str = DIGIT_STR_LIST[digit] if digit < len(DIGIT_STR_LIST) else ''
            wight_str = WIGHT_STR_LIST[wight] if wight < len(WIGHT_STR_LIST) else ''
            #
            if digit is 0:
                wight_str = ''
            # 结果拼接
            result_list.append(digit_str + wight_str)
        # 将结果倒序拼接
        result_list.reverse()
        return "".join(result_list)
    

    OK. 尝试一下:

    • 100 -> 一百
    • 1201 -> 一千二百零一 nice
    • 101 -> 一百零一
    • 1000 -> 一千
    • 100000000 -> 一亿万 什么鬼?

    后边怎么多了一个?

    第五次尝试

    有了处理0的经验, so easy, num_to_str这个函数呀加上一个对0的处理就好了. 代码如下(只展示了num_to_str函数):

    def num_to_str(num : int) -> str:
        """
        将数组装成中文
        :param num:
        :return:
        """
        # 将数字切割为每四个一组, 分别进行处理
        num_list = list(str(num))
        # 这里为了处理长度不是4整数倍情况, 提前反转.
        num_list.reverse()
        group_num_list = [num_list[i:i+4] for i in range(0, len(num_list), 4)]
        result_list = []
        # 遍历每一组, 并产生对应中文输出
        for group, num_list in enumerate(group_num_list):
            # 若是0, 跳过
            if int(''.join(num_list)) is 0:
                continue
            this_num_str = thousand_list_num_to_str(num_list)
            group_str = GROUP_STR_LIST[group] if group < len(GROUP_STR_LIST) else ''
            result_list.append(this_num_str + group_str)
        result_list.reverse()
        return ''.join(result_list)
    

    再次进行尝试:

    • 100000000 -> 一亿 nice!!
    • 0 -> ??? 我的零呢?

    第六次尝试

    这个判断就粗暴了, 直接在num_to_str的入口处强制判一下0, 改动内容:

    ZERO_STR = '零'
    def num_to_str(num : int) -> str:
        if num is 0:
            return ZERO_STR
        ...
    

    再来:

    • 0 ->
    • ...

    经过我的一番测试, 基本完成.

    总结

    开始有这个想法的时候, 我想着会很简单, 随便写写咯. 但是当真正开始动手后, 才发现, 事情完全偏离了我的预期. 在写的过程中, 初版只是个很简单的版本, 但是在自己尝试的过程中总是发现各种各样的问题, 甚至有的时候解决了这个问题, 回头一测, 发现原来已经改好的问题有出现了, 唉, 果然还是功力太浅啊. too young, too simple, sometimes naive.

    我最终还算是磕磕绊绊的写完了, 不过冥冥之中还是感觉有一些情况没有考虑到, 无妨, 反正这不过是个一路填坑的过程, 再碰到问题, 改就完了.


    至此, 代码初步完成, 将完整代码奉上:

    # 数字中文
    DIGIT_STR_LIST = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九']
    # 权重中文
    WIGHT_STR_LIST = ['', '十', '百', '千']
    # 分组后对应的中文
    GROUP_STR_LIST = ['', '万', '亿', '兆']
    # 零
    ZERO_STR = '零'
    
    
    def thousand_list_num_to_str(num_list: list) -> str:
        """
        将4位数字转成字符串
        :param num_list: 数字列表, 长度不超过4. 索引和数字对应为: 个十百千
        :return:
        """
        # 保存每一位的内容
        result_list = []
        # 遍历数字的每一位, 将数组转列表并倒序遍历
        for wight, digit in enumerate(num_list):
            digit = int(digit)
            if digit is 0:
                # 个位的0无输出
                if wight is 0:
                    continue
                # 连续0无输出
                elif int(num_list[wight-1]) is 0:
                    continue
                # 直接拼零
                result_list.append(ZERO_STR)
                continue
            digit_str = DIGIT_STR_LIST[digit] if digit < len(DIGIT_STR_LIST) else ''
            wight_str = WIGHT_STR_LIST[wight] if wight < len(WIGHT_STR_LIST) else ''
            #
            if digit is 0:
                wight_str = ''
            # 结果拼接
            result_list.append(digit_str + wight_str)
        # 将结果倒序拼接
        result_list.reverse()
        return "".join(result_list)
    
    
    def num_to_str(num : int) -> str:
        """
        将数组装成中文
        :param num:
        :return:
        """
        if num is 0:
            return ZERO_STR
        # 将数字切割为每四个一组, 分别进行处理
        num_list = list(str(num))
        # 这里为了处理长度不是4整数倍情况, 提前反转.
        num_list.reverse()
        group_num_list = [num_list[i:i+4] for i in range(0, len(num_list), 4)]
        result_list = []
        # 遍历每一组, 并产生对应中文输出
        for group, num_list in enumerate(group_num_list):
            # 若是0, 跳过
            if int(''.join(num_list)) is 0:
                continue
            this_num_str = thousand_list_num_to_str(num_list)
            group_str = GROUP_STR_LIST[group] if group < len(GROUP_STR_LIST) else ''
            result_list.append(this_num_str + group_str)
        result_list.reverse()
        return ''.join(result_list)
    
  • 相关阅读:
    JavaScript(第五天)【流程控制语句】
    JavaScript(第四天)【运算符】
    JavaScript(第二天)【语法,变量】
    JavaScript(第一天)【<script>标签浅析】
    JavaScript(简介)【Javascript历史】
    [LeetCode-JAVA] Substring with Concatenation of All Words
    [LeetCode-JAVA] Reverse Nodes in k-Group
    [LeetCode-JAVA] Median of Two Sorted Arrays
    [LeetCode-JAVA] Contains Duplicate IIIMaximal Square
    [LeetCode-JAVA] Contains Duplicate III
  • 原文地址:https://www.cnblogs.com/hujingnb/p/13193030.html
Copyright © 2011-2022 走看看