好久没写东西了,除了确实比较摆的主观原因之外还有要搞那个SMP 2023的复赛的客观原因。那个项目写着感觉也还学到或者说复习了不少东西,包括正则表达式、操作数据库还有用HuggingFace库对生成式模型的生成参数做调整等,这些以后有时间单独写一篇。

今天这篇只谈一个很小的点,就是假设随便给一个字符串,如何判断里面的文本是不是数字呢?

这个方案其实还挺多,最简单的就是直接float强转一下然后错误处理,出错的肯定就不是数字了。但实际上,Python已经在字符串里给出了很方便的函数来判断是不是数字,但是也比较容易混淆,即isnumericisdigit方法。

两者的异同

首先先说一下两者的共同点,就是它们对于阿拉伯整数都有很好地判断能力,比如:

1
2
3
a = "12345"
a.isnumeric(), a.isdigit()
# True, True

但是isnumeric的范围是更广的,它甚至可以识别中文的数字表达:

1
2
3
a = "三百二十一"
a.isnumeric(), a.isdigit()
# True, False

这非常的夸张,如果我们只想要阿拉伯数字构成的文本,用isnumeric方法之后会把汉字的数字字符也当成数字,在后面要进行转换的时候就可能出问题,所以这种情况下我们应该用isdigit

二者不是完备的数字检测方法

那是不是可以这样说呢,如果我们有处理中文表达的数字的能力,我们就可以用isnumeric把中文数字字符串放进来,反之如果我们只想要阿拉伯数字组成的字符串,就使用isdigit呢?

遗憾地是,似乎isnumeric并不能查全中文的数字表达,比如一个很常见的表达”两百“,在该方法中就会返回False,因为该方法似乎并不认为”两“是数字。

1
2
3
4
a = "两百"
b = "两"
a.isnumeric(), b.isnumeric()
# False, False

这会把很多含“两”说法的中文数字拒之门外,因此,isnumeric方法并不能很完备地识别中文数字的表达,可能需要增加一些额外的规则才能让它变得好用。

isdigit呢,在int类型上似乎没啥问题,但同样它不能识别小数。当然这是吹毛求疵的要求,因为在英文中,"digit"通常指的是整数位数,不包括小数。所以如果你想检测一个字符串是不是阿拉伯数字组成的合法小数,那就用float尝试转换一下就可以了。

但是有实用性

尽管都不能涵盖所有情况,但是如果限制应用场景,这两个方法都能成为相当有用的方法。比如我们定义一个能查找中文和阿拉伯数字的正则表达式:

1
2
3
import re

a = re.compile("[一二两三四五六七八九十百千亿\d]+")

现在这个正则表达式能够匹配中文和阿拉伯数字,但离真正转换成int类型还有一段距离。因为由数字1-9组成的字符串只需要用int转换一下即可,但中文数字还需要进一步处理一下。因此此时isdigit方法就能充当这个路由的作用。

1
2
3
4
5
def str2int(string: str):
if string.isdigit():
return int(string)

return chinese_to_number(string)

可以实现一个中文转数字的函数:

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
def chinese_to_number(chinese_str):
'''增强版的中文数字转int类型,支持十位及以上的中文表示转阿拉伯数字表示'''
chinese_numbers = {
'零': 0, '一': 1, '二': 2, '两': 2, '三': 3, '四': 4,
'五': 5, '六': 6, '七': 7, '八': 8, '九': 9
}

chinese_units = {
'十': 10, '百': 100, '千': 1000, '万': 10000, '亿': 100000000
}

number = 0
temp_number = 0
temp_unit = 1

for char in chinese_str:
if char in chinese_numbers:
temp_number = chinese_numbers[char]
elif char in chinese_units:
temp_unit = chinese_units[char]
if temp_unit == 10: # 处理十的情况
if temp_number == 0: # 十位前没有数字,默认为1
temp_number = 1
number += temp_number * temp_unit
temp_number = 0
else:
number += temp_number * temp_unit
temp_number = 0
else:
raise ValueError(f"无法识别的字符: {char}")

number += temp_number
return number

这样两者配合就能实现中文和阿拉伯文数字识别通吃了。

但是这样就完备吗,显然也是不行的,它对错误的表达天然没有抵御能力。比如“三百七千六”,它不会有告警信息而是直接转换为"7306"了,这个很难说是错是对,因为表达就不对。然后第二点是对中文和阿拉伯数字混杂的表达没有处理,比如"3百"这样的表达它也是无能为力的,但是它会报错而不是直接入库,这可以给使用者处理的空间。

小结

总结一下,如果要提取的信息确定是合规的,这些函数配合起来确实是实用的。但不要指望它们能够处理所有的问题。第二点就是对于类似的函数还是要注意一下区别,以防止误用。