用 Python 快速转化「中文数字」和「阿拉伯数字」

您所在的位置:网站首页 数字翻译中文对照表 用 Python 快速转化「中文数字」和「阿拉伯数字」

用 Python 快速转化「中文数字」和「阿拉伯数字」

2023-08-17 16:24| 来源: 网络整理| 查看: 265

最初,我是在开发聊天机器人的时候用到这个功能,比如用户提问 一千米以内有哪些场地可用?,我需要在数据库中查询范围小于一千米的场地,SQL 语句大致为 WHEN distant ten_thousand_unit: ten_thousand_unit = unit # 万亿 else: ten_thousand_unit = unit * ten_thousand_unit unit = ten_thousand_unit if unit < ten_thousand_unit: unit = ten_thousand_unit * unit else: raise ValueError(f"{cn_num} 不在转化范围内") return output output = backward_cn2an_three("一千二百三十四万五千六百七十八亿一千二百三十四万五千六百七十八") # output: 1234567812345678

至此,我们的反向遍历算法也完成了,本来想省掉一个变量,似乎最后没有做到...

如你所见,这两个算法的时间复杂度都是 O(n),空间复杂度都是 O(1),本质上并没有什么优劣,但我依然感觉反向遍历在思维上流畅很多!因此我在 cn2an 这个库中用的就是反向遍历法啦!

另外,这里有上文的所有代码。

3 模式匹配

如果你给正确的算法丢进一个错误的输入,那么你大概率得到也是一个错误的输出!

郭冬临:用谎言验证谎言,得到的一定是谎言!

比如我们把 十七十八 输入上述两个算法的最终版本,你可能期望的结果是 1718,而实际上会是 78,具体原因我就不细说了,你可以自己思考一下。

output = forward_cn2an_three("十七十八") # output: 78 output = backward_cn2an_three("十七十八") # output: 78

因此我们必须要限制输入,所有格式不正确的输入都要排除在外。俗话说的好:人生苦短,我用正则!,我们现在就来写个正则表达式。

先给大家看我写的第一版有问题的模式:

import re nums = "零一二三四五六七八九" nums_ten = "零一二三四五六七八九十" units = "十百千万亿" pattern = re.compile(f"(([{nums_ten}]+[{units}]+)+零?[{nums}]|([{nums_ten}]+[{units}]+)+|十[{nums}]?|[{nums}])$") result = pattern.search("十七十八") if result: print(result.group()) # 十七十八

这个正则看似很简单,实则问题非常大。我的思路是匹配带零的数字(比如一千零二十三)、不带零的数字(比如一百二十三)、十几(比如十一)和几(比如一)的任意一种,但它会把很多有问题的模式匹配进来,比如刚刚的 十七十八 这种,反正问题很多!

后来这个问题一直有人向我提,虽然我知道这不是算法的问题,但却是这个库需要解决的问题。有一天晚上,我苦苦想了好几个小时,尝试很多方法,最后想到一个笨方法——枚举所有需要匹配的数字!

import re _0 = "[零]" _1_9 = f"[一二三四五六七八九]" _10_99 = f"{_1_9}?[十]{_1_9}?" _1_99 = f"({_10_99}|{_1_9})" _100_999 = f"({_1_9}[百]([零]{_1_9})?|{_1_9}[百]{_10_99})" _1_999 = f"({_100_999}|{_1_99})" _1000_9999 = f"({_1_9}[千]([零]{_1_99})?|{_1_9}[千]{_100_999})" _1_9999 = f"({_1000_9999}|{_1_999})" _10000_99999999 = f"({_1_9999}[万]([零]{_1_999})?|{_1_9999}[万]{_1000_9999})" _1_99999999 = f"({_10000_99999999}|{_1_9999})" _100000000_9999999999999999 = f"({_1_99999999}[亿]([零]{_1_99999999})?|{_1_99999999}[亿]{_10000_99999999})" _1_9999999999999999 = f"({_100000000_9999999999999999}|{_1_99999999})" pattern = re.compile(f"^({_0}|{_1_9999999999999999})$") result = pattern.search("十七十八") if result: print(result.group()) # 输出为空,因为没有匹配到

看到上面一层又一层的嵌套,我想你应该猜到这个正则表达式会特别长,居然达到了 5323 个字符...令我惊讶的是,它匹配起来依然很快,对性能来说几乎没有影响。

目前为止,我依然是个正则新手,上面的解决方法肯定不是最优的,大家如果有好的方法可以提 PR ,或者开 Issue 和我讨论,一起精进!

4 cn2an 介绍

如果你的需求简单,可以直接把上面代码嵌入到自己的程序中,如果你想有更多的功能,来试试 cn2an 吧!

???? cn2an 是一个快速转化 中文数字 和 阿拉伯数字 的工具包!

4.1 功能

支持 中文数字 => 阿拉伯数字;

支持 大写中文数字 => 阿拉伯数字;

支持 中文数字和阿拉伯数字 => 阿拉伯数字;

支持 阿拉伯数字 => 中文数字;

支持 阿拉伯数字 => 大写中文数字;

支持 阿拉伯数字 => 大写人民币;

支持 中文数字 的句子 => 阿拉伯数字 的句子;

支持 阿拉伯数字 的句子 => 中文数字 的句子;

支持 小数;

支持 负数;

支持 HTTP API。

4.2 安装 # pip 安装 pip install cn2an -U #从代码库安装 git clone https://github.com/Ailln/cn2an.git cd cn2an && python setup.py install 4.3 使用 # 在文件首部引入包 import cn2an # 查看版本 print(cn2an.__version__) # 0.5.2 # 在 strict 模式(默认)下,只有严格符合数字拼写的才可以进行转化 output = cn2an.cn2an("一百二十三") # 或者 output = cn2an.cn2an("一百二十三", "strict") # output: # 123 # 在 normal 模式下,还可以将 一二三 进行转化 output = cn2an.cn2an("一二三", "normal") # output: # 123 # 在 smart 模式下,还可以将混合拼写的 1百23 进行转化 output = cn2an.cn2an("1百23", "smart") # output: # 123 # 以上三种模式均支持负数 output = cn2an.cn2an("负一百二十三", "strict") # output: # -123 # strict 和 normal 模式支持小数,smart 模式暂不支持 output = cn2an.cn2an("一点二三", "strict") # output: # 1.23

更多使用方法请访问 https://github.com/Ailln/cn2an。

长按扫码添加“Python小助手” 

进入 P Y 交 流 群

▼点击成为社区会员   喜欢就点个在看吧



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3