之前参加了hackergame2020,现在来聊聊做出来的几道有意思的题。希望以后能做出更多道。(业余ctfer)
其中有一道《从零开始的记账工具人》比较有意思。想做的应该能做出来(有code基础)。
给我们一份excel,如图:

有1000行,这个账单就不是让你手动计算的。
首先一般碰见这种都是先上网查一下有没有大写中文金额转阿拉伯数字的,还真让我找着个,可惜不是准确的。地址如下:
https://www.wntool.com/numtohz/

这个没有小数位,而且一些转换也不是很准确,所以不好使。
然后有找了一个excel插件(方方格子),能支持转换的,可惜也不支持小数位。
没折,只好自己code实现转换了。coding前先分析算法。我是直接在纸上画的:
先列出几个人民币金额,然后对比分析。这里采用最简单的。先按【亿,万,元,角,分】划分。再按【亿万仟佰拾个】位数分,位数就没角分什么事了。
写出的代码也很简单,虽然比较长,但是很好理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#中文转数字
def zhToNum(zhStr):
if (zhStr == ''):
return '0'
resnum = ''
zhNumber = '零壹贰叁肆伍陆柒捌玖'
for zhnum in zhStr:
num = zhNumber.find(zhnum)
if (num != -1):
zhStr = zhStr.replace(zhnum, str(num))
resnum = str(num)
else:
resnum = '0'
return resnum

def tonumber(zhStr):
ge = '0'
shi = '0'
bai = '0'
qian = '0'
qianIndex = zhStr.find('仟')
if (qianIndex != -1):
qian = zhStr[0:qianIndex]
qian = zhToNum(qian)
zhStr = zhStr[qianIndex+1:strLen]

baiIndex = zhStr.find('佰')
if (baiIndex != -1):
bai = zhStr[0:baiIndex]
bai = zhToNum(bai)
zhStr = zhStr[baiIndex+1:strLen]

shiIndex = zhStr.find('拾')
if (shiIndex != -1):
shi = zhStr[0:shiIndex]
shi = zhToNum(shi)
zhStr = zhStr[shiIndex+1:strLen]

ge = zhToNum(zhStr)

return float(qian)*1000+float(bai)*100+float(shi)*10+float(ge)


array = []
with open('./file.txt', encoding='utf-8') as lines: #一次性读入txt文件,并把内容放在变量lines中
array=lines.readlines() #返回的是一个列表,该列表每一个元素是txt文件的每一行
lines.close()

for tempStr in array:
yi = '0'
wan = '0'
yuan = '0'
jiao = '0'
fen = '0'
strLen = len(tempStr)
yiIndex = tempStr.find('亿')
if (yiIndex != -1):
yi = tempStr[0:yiIndex]
tempStr = tempStr[yiIndex+1:strLen]

wanIndex = tempStr.find('万')
if (wanIndex != -1):
wan = tempStr[0:wanIndex]
tempStr = tempStr[wanIndex+1:strLen]

yuanIndex = tempStr.find('元')
if (yuanIndex != -1):
yuan = tempStr[0:yuanIndex]
tempStr = tempStr[yuanIndex+1:strLen]

jiaoIndex = tempStr.find('角')
if (jiaoIndex != -1):
jiao = tempStr[0:jiaoIndex]
tempStr = tempStr[jiaoIndex+1:strLen]

fenIndex = tempStr.find('分')
if (fenIndex != -1):
fen = tempStr[0:fenIndex]
tempStr = tempStr[fenIndex+1:strLen]

yi = tonumber(yi)*100000000
wan = tonumber(wan)*10000
yuan = tonumber(yuan)
jiao = tonumber(jiao)*10
fen = tonumber(fen)

with open('./out.txt', 'a', encoding='utf-8') as fw:
fw.write(str(int(yi+wan+yuan))+'.'+str(int(jiao+fen)))
fw.write('\n')
fw.close()

发送20201129获取源代码。(python3哦)

之后看了官方的writeup,官方给出了三种解法。感兴趣的可以往下看:

解法1

手工计算,估计是逗你的,官方很皮。

解法2

使用任意文本编辑器(或者 Excel 本身)做字符串替换,替换规则如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
'零' -> ''
'壹' -> '1'
'贰' -> '2'
'叁' -> '3'
'肆' -> '4'
'伍' -> '5'
'陆' -> '6'
'柒' -> '7'
'捌' -> '8'
'玖' -> '9'
'拾' -> '*10+'
'佰' -> '*100+'
'仟' -> '*1000+'
'元' -> '+'
'角' -> '/10+'
'分' -> '/100'
'++' -> '+'
'整' -> ''

然后如果开头有乘号或者结尾有加号,去掉即可,这样的数学表达式求值即可得到正确的结果。

这个就比较神奇了,老实说我甚至没有往这方向想。老实说excel达人还是很牛逼的。

解法3

编程求解,这里使用 Python 语言。 (正题了)
在 Python 中安装 cn2an 这个中文数字转换的库:
python3 -m pip install cn2an
然后使用 Python 程序处理这个文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import cn2an
lines = open('bills.csv').readlines()[1:]
s = 0
for line in lines:
a, b = line.strip().split(',')
n = 0
if '元' in a:
y, a = a.split('元')
n += cn2an.cn2an(y, "smart")
if '角' in a:
y, a = a.split('角')
n += cn2an.cn2an(y, "smart") / 10
if '分' in a:
y, a = a.split('分')
n += cn2an.cn2an(y, "smart") / 100
s += n * int(b)
print(s)

最后输出的结果可能有一些浮点误差,自己四舍五入一下就好了。(更好的办法是使用整数来计算,但是我比较懒就不写了)

完。

希望这篇文章能给你带来知识和乐趣,喜欢博主的文章可以加博主好友哦

有好的文章也可以向博主投稿哦