如何将数字转换成口语中的文本串

概述

今天突发奇想, 写一个将数字转换成中文字符串的函数. 并不是将 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:continuedigit_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)continuedigit_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:continuethis_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)continuedigit_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:continuethis_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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/508415.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式故事会

想着用讲故事的方式对各个设计模式进行一个稍微形象一些的描述. 当然, 某些因为没有想到特别好的例子, 可能比喻的并不是那么的恰当, 望赐教. 绞尽脑汁啊, 尽力贴近设计模式其本来的意图. 主要为了加强一些自己对设计模式的理解. 进入正题: 小王是一名心怀梦想的90后青年, 他独…

有了 elseif 为什么还要 switch case

引出 你有没有想过既然有了if elseif, 为什么还要设计一个switch case的语法出来呢? 按理说, 一个语言的设计角度来说, 关键词越少越好吧, 而且多出来一种选择分支也没有看出太大用处. 以下几种switch case均可以写成if else的形式(java 代码): // 形式一 switch(a){case 1:…

计算矩阵中全1子矩阵的个数

前言 最近被我大哥安利了一道算法题, 这道题说难, 还不至于我做不出来, 说简单吧, 我还想不到最优解, 等把最优解告诉我之后, 我还正好能理解. 我甚至曾经怯怯的认为, 这题就是我哥专门给我找的, 嘿嘿, 心中说不出的小欢喜. 题来了, 此题出自力扣, 原题链接: https://leetco…

GO 的方法集

前言 之前在写 GOdemo 的时候, 写了这么一段程序(大概意思): package maintype Test struct { }func (test *Test) print() {println("test fun") }func main() {Test{}.print() }结果一编译就报错了: cannot call pointer method on Test literal 差不多意思是不…

GO 切片实力踩坑

概述 GO 语言的切片这两天用了用, 可以支持切割数组的中间部分. 但今天使用中, 出了 bug, 查了半天, 发现是切片的问题, 简单写个 demo 复现当时的情况: package mainimport "fmt"func main() {a : []int{1, 2, 3, 4, 5}b : a[2:4]b[0] 9fmt.Println(a) }你以为输…

搭建本地 HTTPS 环境

前言 之前写自己的网站时, 申请过免费的https证书. 最近想在自己本地搭一个, 结果忘了当初证书是怎么来的了. 本来想着去申请个免费的证书, 但想了想, 我只需要在自己本地能使用就行了, 我自己的环境, 那当然是我说了算了. 只要能够将证书构造出来, 安装到本地就可以识别了. 搜…

《人人都是产品经理》读后感

之前读书, 一般看的都是技术、算法、数学、哲学这些方面的, 为什么突然想看这本书呢? 身为一个开发人员, 每天打交道最多的, 除了开发和测试, 就是产品了. 还记得我刚来公司的时候, 在和产品的沟通过程中, 听到最多的话估计就是: 你要懂产品, 你懂了就不会做错. 确实有很多需求…

nginx 端口转发

概述 这两天在写 go 项目, 一个 HTTP 服务器. 之前写的是 php 项目, nginx 监听80端口, 根据域名将请求分配给不同项目. 现在换了 go, 自然也想延续这个操作, 毕竟都是跑在同一台服务器上. 那么问题来了, 我的nginx 监听80端口的同时, go 服务器是无法同样监听80端口的. 这该如…

2020-08-20

前段时间有些迷茫, 有时看着自己写的代码感觉就像一坨屎, 有时写了一段逻辑总是感觉有问题, 测了一遍又一遍, 逻辑也要理好几次. 就像是进入了一种不可描述的状态, 这感觉让我很不自在. 敲代码的时间说长不长, 说短不短, 从大二开始, 到现在也小四年了, 还没有碰到过这种情况. …

beego 优雅重启

前言 最近在写 go 的项目, http 用的 beego 框架. 因为 go 不想 php, 每次代码改动都需要重启服务, 所以代码发上线之后, 如何重启服务就成了一个问题. 如果强行重启的话, 不光在重启期间的所有访问都被拒绝了, 而且在杀掉进程的时候处理中的请求也挂了. 对于一个向用户正常提…

由 go orm 引发的探索

前言 今天遇到了一个 bug, 是 golang 的orm导致的. 使用了gorm框架. 通过实现Scan与Value可以将数据库中的 json 内容解析出来, 免除了 字符串再解码的步骤. 当时报错的代码大概是这样的: type TestContent struct {Id intContent Content // 数据库中的 json 结构 }type Con…

MySQL 数据恢复

MySQL 数据恢复 前言 前两天因为没注意的误操作, 直接把某个数据表清掉了, 心慌慌. 怪自己学艺不精, 当时整了一下午也没把数据找回来. 当晚回来闭关研究, 终于在凌晨1点多整出来了, 特此记录, 以备不时之需. 对于 MySQL 数据的备份, 主要有两种: 全量备份和增量备份. 全量…

码云 Pages 搭建

因为一直在写博客, 就向着搭个 GithubPages 来展示, 一直都听说别人用它来搭建个人博客, 但一直停留在听说的阶段. 最近想着没事搞一搞, 也看看它到底是个什么东东. 不过咱一个写中文博客的, 就想着在码云上搭一个, 顺便还能被百度收录, 嘿嘿. 说干就干, 开搞. (Pages 服务只能…

crontab 指令笔记

前言 之前有个定时任务, 需要每天的9点和21点定时跑. 当时我是这么写的: 0 9 * * * XXX 0 21 * * * XXX当时经过高人指点, 发现这两条命令可以写成一条: 0 9,21 * * * XXX好羞耻… 于是, 我决定整理一篇crontab的语法. 防止下次再出现这尴尬的场景… 指令 命令行 crontab…

golang 多协程的同步方法总结

之前用 go 写一个小工具的时候, 用到了多个协程之间的通信, 当时随手查了查, 结果查出来一大坨, 简单记录一下. golang中多个协程之间是如何进行通信及数据同步的嘞. 共享变量 一个最简单, 最容易想到的, 就是通过全局变量的方式, 多个协程读写同一个变量. 但对同一个变量的更…

golang chan 探究

前言 之前在看golang多线程通信的时候, 看到了go 的管道. 当时就觉得这玩意很神奇, 因为之前接触过的不管是php, java, Python, js, c等等, 都没有这玩意, 第一次见面, 难免勾起我的好奇心. 所以就想着看一看它具体是什么东西. 很明显, 管道是go实现在语言层面的功能, 所以我以…

计算机网络-信道复用技术

还记得计算机网络中的信道复用技术么? 来来来, 一起复习一下. why 问: 什么是信道复用. 在回答这个问题之前先看这样一个场景: 其中u1 u2是两个用户, 如果这两个用户之间连通的信道在他们使用过程中, 被他们完全占用了, 其他人就只能等着了. 那有人说了, 那就多架设信道不就…

IP 数据报首部分析

来来来, 爷们. 不是一直说纸上得来终觉浅么. 今咱就抓个数据报具体看一看真实网络中的 IP 报首部. 操作方法很简单, 使用wireshark进行抓包. 抓包后随便找个包看一下就行, 毕竟所有通信的包都需要经过网络层.(同时, wireshark会对协议的相关信息给出标识, 更方便我们查看) 其中…

git 子模块在项目中的使用

在公司的项目中, 经常会遇到一些公共的内容, 多个项目中间通用的, 不可能每次都将整个代码复制一遍, 遇到这种情况有很多不同的解决方案, 一般来说, 项目是通过 git 来管理的, 巧了, git 也同样支持子模块. 创建子模块 git submodule add gitgitee.com:hujingnb/submodule_so…

Hello IPv6

前言 IPv4 大家都不陌生, 就是我们平常使用的32位网络地址. 但是, 因为可能在其发展初期, 并没有考虑的十分全面, 故而其存在一些缺陷: 32位二进制仅支持约43亿个设备, 还不到全球的人口数量. (故而现在依赖于各种局域网转换技术, 比如 NAT. 否则地址早就不够用了. 但是 NAT …