在实际数据处理场景中,我们经常会遇到需要排序的字符串里同时包含中文数字和阿拉伯数字的情况,比如章节名、序号列表等。默认的字符串排序会按照字符的Unicode编码顺序处理,完全无法识别数字的大小含义,因此需要自定义排序规则来完成这类需求。

问题分析
先看一个常见的例子,我们需要排序的字符串列表如下:
test_list = ["第1章", "第3章", "第2章", "第10章", "第5章", "第两百章", "第一百章", "第12章"]
如果直接使用sorted()函数排序,得到的结果会是["第1章","第10章","第12章","第2章","第3章","第5章","第两百章","第一百章"],显然不符合数字大小的逻辑,而且中文数字完全没有参与正确的大小比较。
核心解决思路
要实现正确的排序,核心思路是提取字符串中的数字部分,统一转换为阿拉伯数字,再作为排序的键。整体步骤分为三步:
- 识别字符串中的阿拉伯数字和中文数字
- 将识别到的数字统一转换为阿拉伯整数
- 自定义排序的键函数,使用转换后的数字作为排序依据
中文数字转换为阿拉伯数字
首先我们需要实现一个函数,把中文数字(比如"一百""两百""三千零五")转换为对应的阿拉伯数字。这里我们需要考虑中文数字的单位:个、十、百、千、万、亿等,以及常见的组合规则。
def chinese_to_arabic(chinese_num):
# 中文数字到对应数值的映射
num_map = {
"零": 0, "一": 1, "壹": 1, "二": 2, "贰": 2, "两": 2,
"三": 3, "叁": 3, "四": 4, "肆": 4, "五": 5, "伍": 5,
"六": 6, "陆": 6, "七": 7, "柒": 7, "八": 8, "捌": 8,
"九": 9, "玖": 9, "十": 10, "拾": 10, "百": 100,
"佰": 100, "千": 1000, "仟": 1000, "万": 10000,
"萬": 10000, "亿": 100000000, "億": 100000000
}
# 单位列表,按从大到小排序
units = [("亿", 100000000), ("万", 10000), ("千", 1000), ("百", 100), ("十", 10)]
result = 0
temp_num = 0
i = 0
length = len(chinese_num)
while i < length:
char = chinese_num[i]
# 如果是数字字符
if char in num_map and num_map[char] < 10:
temp_num = num_map[char]
# 如果下一个字符是单位,且单位小于10,说明是"十几"的情况
if i + 1 < length and chinese_num[i+1] in num_map and 10 <= num_map[chinese_num[i+1]] < 100:
temp_num = temp_num * num_map[chinese_num[i+1]]
i += 1
i += 1
# 如果是单位字符
elif char in num_map and num_map[char] >= 10:
unit_val = num_map[char]
# 处理"十万""百万"这类情况
if temp_num == 0 and i + 1 < length and chinese_num[i+1] in num_map and num_map[chinese_num[i+1]] < 10:
temp_num = 1
result += temp_num * unit_val
temp_num = 0
i += 1
else:
# 非数字字符,跳过
i += 1
# 加上最后剩下的数字部分
result += temp_num
return result提取字符串中的数字
接下来我们需要从字符串中提取出数字部分,同时支持阿拉伯数字和中文数字的识别,统一返回阿拉伯数字结果。
import re
def extract_number(string):
# 匹配阿拉伯数字的正则
arabic_pattern = re.compile(r'\d+')
# 匹配中文数字的正则,覆盖常见数字字符
chinese_pattern = re.compile(r'[零一二三四五六七八九十百千万亿壹贰叁肆伍陆柒捌玖拾佰仟萬億]+')
# 先尝试匹配阿拉伯数字
arabic_match = arabic_pattern.search(string)
if arabic_match:
return int(arabic_match.group())
# 再尝试匹配中文数字
chinese_match = chinese_pattern.search(string)
if chinese_match:
return chinese_to_arabic(chinese_match.group())
# 如果没有数字,返回0作为兜底
return 0实现自定义排序
有了提取数字的函数之后,我们只需要把extract_number作为sorted函数的key参数,就可以实现正确的排序了。
def sort_mixed_strings(string_list):
return sorted(string_list, key=lambda x: extract_number(x))
# 测试示例
test_list = ["第1章", "第3章", "第2章", "第10章", "第5章", "第两百章", "第一百章", "第12章"]
sorted_result = sort_mixed_strings(test_list)
print(sorted_result)运行上述代码,得到的输出结果是:["第1章","第2章","第3章","第5章","第10章","第12章","第一百章","第两百章"],完全符合数字大小的逻辑排序。
性能优化建议
如果需要排序的字符串数量很大,可以考虑做两点优化:
- 缓存提取数字的结果,避免重复调用
extract_number函数,比如使用字典存储已经计算过的字符串对应的数字 - 如果中文数字的格式比较固定,可以简化
chinese_to_arabic函数的逻辑,减少不必要的判断分支
这种自定义排序的方式不仅适用于包含中文数字和阿拉伯数字的字符串,也可以扩展到其他混合格式的排序场景,只需要调整数字提取和转换的逻辑即可。