可能有些情况没有考虑到。
源码
python3:
#encoding=utf-8
import math
#系数
CN_NUM = {
'〇': 0,
'一': 1,
'二': 2,
'两': 2,
'三': 3,
'四': 4,
'五': 5,
'六': 6,
'七': 7,
'八': 8,
'九': 9,
'零': 0,
}
#基数
CN_UNIT = {
'十': 10,
'百': 100,
'千': 1000,
'万': 10000,
'亿': 100000000,
}
#计算中文数字值,返回string
def calcCNNumberValue(digs):
if(not digs): return ""
#根据系数、基数map将汉字转数值大小
tmp =[]
for d in digs[::-1]:
if(CN_NUM.__contains__(d)): tmp.append(CN_NUM[d])
elif(CN_UNIT.__contains__(d)): tmp.append(CN_UNIT[d])
#系数直接加入tmp2,基数相邻相乘或者加入tmp2并在前面补1
tlen = len(tmp)
tmp2 = []
for i in range(0, tlen):
if(tmp[i] > 9):
if(i == tlen - 1 or tmp[i + 1] > tmp[i]):
tmp2.append(tmp[i])
tmp2.append(1)
elif(tmp[i + 1] > 9):
tmp[i + 1] *= tmp[i]
else: tmp2.append(tmp[i])
else: tmp2.append(tmp[i])
#系数直接加入seq,基数根据其大小用-1占住位置让下一个系数在正确位上
seq = []
curW = 0
for t in tmp2:
if(t > 9):
w = math.log10(t)
while(curW < w):
curW += 1
seq.append(-1)
else:
curW += 1
seq.append(t)
#对于个位是非0的前方是-1的数,要尽可能提到高位
if(seq[0] > 0 and len(seq) > 1 and seq[1] == -1):
seqLen, p = len(seq), 1
while(p < seqLen and seq[p] == -1): p += 1
#swap
seq[p - 1] = seq[0]
seq[0] = 0
#seq拼接,-1转为0,其余的保持原数值
return "".join([str(n if n >= 0 else 0) for n in seq[::-1]])
#测试
if __name__ == '__main__':
print(calcCNNumberValue("零")) #0
print(calcCNNumberValue("五")) #5
print(calcCNNumberValue("十")) #10
print(calcCNNumberValue("一十")) #10
print(calcCNNumberValue("十五")) #15
print(calcCNNumberValue("三十五")) #35
print(calcCNNumberValue("三百五")) #350
print(calcCNNumberValue("三千五")) #3500
print(calcCNNumberValue("三千零五")) #3005
print(calcCNNumberValue("三千五十")) #3050
print(calcCNNumberValue("三千五百五")) #3550
print(calcCNNumberValue("二百万")) #2000000
print(calcCNNumberValue("二百万零五十")) #2000050
print(calcCNNumberValue("一万一")) #11000
print(calcCNNumberValue("一万一十")) #10010
print(calcCNNumberValue("一万一百")) #10100
print(calcCNNumberValue("一万一百一")) #10110
print(calcCNNumberValue("一万一百零一")) #10101
print(calcCNNumberValue("一万零一")) #10001
print(calcCNNumberValue("二〇一九")) #2019
print(calcCNNumberValue("千万万")) #100000000000
print(calcCNNumberValue("一千万一")) #11000000
print(calcCNNumberValue("一千万零一")) #10000001
说明
算法分几步。
以几个例子来说明。
第一步,将汉字转为数值,在系数map和基数map中搜索,非法字符在这里会直接被过滤。digs倒着循环,则原先数值的低位会存在tmp数组的低位中。
例如:
“一百万零六” ,转换后存tmp中,tmp为: [6, 0, 10000, 100, 1]
"一百十一“, -> tmp : [1, 10, 100, 1]
"一万一", -> tmp : [1, 10000, 1]
第二步,将基数加入tmp2并在前补1,或者和前基数合并,系数直接加入tmp2。(解决“十一”, “十”这种基数前没系数的情况)
例如:
“一百万零六” -> tmp2 : [6, 0, 1000000, 1] (10000 < 100, 10000被乘到下一个100上并被丢弃)
"一百十一“, -> tmp2 : [1, 10, 1, 100, 1] (10<100,10前补1)
"一万一", -> tmp2 : [1, 10000, 1]
第三步,基数变现,基数的存在是为了下一个系数能够在正确的位置上。遇到系数,直接丢入seq,遇到基数,丢弃或者,在中间补位 (此处补-1,为了和“零”作区别),让下一个系数位置符合这个基数的描述。
例如:
“一百万零六” -> seq : [6, 0, -1, -1, -1, -1, 1] (1000000取log是6,下一个系数必须在第6+1位上(第一个是第1位),中间补-1,直到当前seq长度为6)
"一百十一“, -> seq : [1, 1, 1] (10取log是1,直接丢弃,100取log是2,直接丢弃)
"一万一", -> seq : [1, -1, -1, -1, 1] (10000取log是4,中间补-1,直到当前seq长度为4,下一个1插到第5位上,表示“万”)
第四步,最低位非零数字满足条件可升高位,前面补-1而不补0的作用在此体现。(针对,一万二,两百二等情况,最低位的“二”应分别指代“二千”和“二十”,要尽可能往上升高位)
例如:
“一百万零六” -> seq : [6, 0, -1, -1, -1, -1, 1] (最低为6紧挨着0,说明中文描述时指定了“零”,不做处理)
"一百十一“, -> seq : [1, 1, 1] (最低为1,相邻1,不做处理)
"一万一", -> seq : [-1, -1, -1, 1, 1] (最低位1,相邻着-1,则向高位找,直到非-1为止,找到第4位(第一个算第1位),将第1个和第4个数值交换,“1”由个位升至千位)
第五步,seq倒过来转为string。seq地位存数值地位,所以低位要放右侧,倒着循环。-1变成0,不小于0的保留原值。
例如:
“一百万零六” -> return : 1000006
"一百十一“, -> return : 111
"一万一", -> return : 11000