概述
今天突發奇想, 寫一個將數字轉換成中文字符串的函數. 並不是將 1234
轉成 '1234'
, 而是將 1234
轉成 '一千二百三十四'
.
本來以爲很簡單, 寫下來之後發現還是有些坑的.
嘗試
因爲我是在寫完最終版本, 回過頭來整理的這篇文章, 所以中間很多嘗試的步驟會有所遺漏. 以下簡單整理一下. 如果不想看, 可以直接拉到最後, 看最終的成品.
第一次嘗試
在寫之前, 首先要尋找中文說話的規律嘛.
- 數字的念法:
零一二三四五六七八九
- 每一位都有一個對應的權重:
個十百千萬
所以我的初步想法是, 將數字的每一位都轉成中文然後拼上對應的權重, 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
->一千二百零一
nice101
->一百零一
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)