前言
在财务系统、发票ocr识别、报销单据解析、合同文本抽取等业务中,经常会遇到中文大写人民币金额:比如“壹拾万零捌佰元整”“负贰亿零伍万元”,业务需要把它转换成可计算的阿拉伯小数用于对账、汇总、统计。
市面上很多简易转换脚本存在大量缺陷:
- 无法处理亿、万多级大数,计算逻辑错乱;
- 不支持「负、调减」负数场景;
- 无法兼容全角字符、圆/元混用、末尾“整”;
- 缺少大写合法性校验,脏数据直接转换导致金额错乱;
- 不能从长文本中自动截取大写金额片段。
本文提供一套生产级完整代码,包含四大能力:全角转半角、文本抽取大写金额、大写转数字、财务大写规范校验,附带详细原理、坑点说明与测试用例,拿来即可集成到erp、票据识别、python数据分析项目。
一、财务大写基础规范(先懂规则再写代码)
1. 合法字符集
数字:零、壹、贰、叁、肆、伍、陆、柒、捌、玖
单位:拾、佰、仟、万、亿、元(圆)、角、分、整
规范前缀:人民币
2. 硬性书写规则(代码校验逻辑来源)
- 标准大写必须以「人民币」开头;
- 禁止简体数字(一/二/十)、阿拉伯数字、特殊符号;
- 拾位不能单独出现,必须写「壹拾」,禁止“拾元、拾万”;
- 不能连续多个零(零零);
- 末尾带有「分」时,不能再加「整」;
- 金额分为三段:整数元部分、角、分;
- 负数业务标识:文本含「负」「调减」代表金额为负。
二、整体架构说明
整套工具拆分为三层,低耦合、易维护:
- 预处理层:全角转半角、正则截取文本中的大写金额片段、负数标记识别;
- 转换核心层:
cnmoney_to_num,纯大写字符串转浮点数(单位元),支持亿/万/角/分; - 校验层:
check_fin_capital,校验大写是否符合财务规范,返回错误原因; - 业务封装入口:
get_payment_fix,对外统一调用,适配原始长文本输入。
三、完整可运行源码
# -*- coding: utf-8 -*-
# 财务人民币大写金额转换+规范校验完整工具类
import re
# 正则:匹配文本中“大写”到括号的金额片段,用于长文本抽取
re_big_write_content = re.compile('大写[-\d\d]*?)')
def q2b(uchar):
"""单个字符 全角转半角"""
inside_code = ord(uchar)
if inside_code == 0x3000:
inside_code = 0x0020
else:
inside_code -= 0xfee0
if inside_code < 0x0020 or inside_code > 0x7e:
return uchar
return chr(inside_code)
def stringq2b(ustring):
"""整段字符串全角转半角,统一文本格式"""
return "".join([q2b(uchar) for uchar in ustring])
def cnmoney_to_num(cn_str: str) -> float:
"""
人民币大写转数字小写(单位:元)核心算法
:param cn_str: 纯财务大写字符串,如 "壹万贰仟叁佰肆拾伍元陆角柒分"
:return: float 金额(元,保留两位小数)
"""
digit_map = {
'零': 0, '壹': 1, '贰': 2, '叁': 3, '肆': 4,
'伍': 5, '陆': 6, '柒': 7, '捌': 8, '玖': 9
}
unit_map = {
'拾': 10, '佰': 100, '仟': 1000,
'万': 10000, '亿': 100000000,
'角': 0.1, '分': 0.01
}
# 清理无关字符
s = cn_str.replace("整", "").replace("圆", "元").strip()
yuan_part = 0
jiao_fen = 0.0
yuan_str = ""
# 分割整数元 和 角分小数部分
if "元" in s:
yuan_str, jf_str = s.split("元", 1)
else:
jf_str = s
# 解析角、分
jiao = 0
fen = 0
if "角" in jf_str:
jiao_part, fen_part = jf_str.split("角", 1)
if jiao_part and jiao_part in digit_map:
jiao = digit_map[jiao_part]
if "分" in fen_part and fen_part.replace("分", "") in digit_map:
fen = digit_map[fen_part.replace("分", "")]
elif "分" in jf_str:
fen_str = jf_str.replace("分", "")
if fen_str in digit_map:
fen = digit_map[fen_str]
jiao_fen = jiao * 0.1 + fen * 0.01
# 核心:解析亿、万、仟、佰、拾整数部分
total = 0
tmp = 0
num_buf = none
for char in yuan_str:
if char in digit_map:
num_buf = digit_map[char]
elif char in unit_map:
u = unit_map[char]
if num_buf is not none:
val = num_buf * u
# 亿、万属于大单位,需要累加前置临时值
if u >= 10000:
total += (tmp + val)
tmp = 0
else:
tmp += val
num_buf = none
# 处理末尾无单位数字(例:壹拾万零捌佰,末尾捌佰无后续单位)
if num_buf is not none:
tmp += num_buf
yuan_part = total + tmp
return round(yuan_part + jiao_fen, 2)
def get_payment_fix(raw_text):
"""
业务层统一入口:原始长文本抽取大写并转换为金额(支持负数)
:param raw_text: 原始单据文本,如 "单据大写:负壹万贰仟叁佰肆拾伍元陆角柒分)"
:return: float 最终金额,负数自动标记
"""
# 1. 预处理:全角转半角统一格式
text = stringq2b(raw_text)
flag = 0
# 识别负数标识
if '调减' in text:
flag = 1
elif '负' in text:
flag = 2
# 正则截取大写片段
big_match = re_big_write_content.search(text)
if big_match:
cn_str = big_match.group(0).replace("大写","").replace(")","").strip()
else:
cn_str = text
# 调用核心转换
num = cnmoney_to_num(cn_str.replace("圆","元"))
# 处理负金额
if flag == 1 or flag == 2:
num = -num
return num
# ------------------- 财务大写合法性校验模块 -------------------
# 1. 定义合法大写汉字
valid_chars = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖",
"拾", "佰", "仟", "万", "亿", "元", "角", "分", "整"}
# 2. 正则匹配标准财务大写完整模板
pattern = re.compile(
r"^人民币"
r"(零|壹[拾佰仟万亿]*|贰[拾佰仟万亿]*|叁[拾佰仟万亿]*|肆[拾佰仟万亿]*|伍[拾佰仟万亿]*|陆[拾佰仟万亿]*|柒[拾佰仟万亿]*|捌[拾佰仟万亿]*|玖[拾佰仟万亿]*)*"
r"(元)?"
r"(零|壹|贰|叁|肆|伍|陆|柒|捌|玖)角?"
r"(零|壹|贰|叁|肆|伍|陆|柒|捌|玖)分?"
r"(整)?$"
)
def check_fin_capital(cap_str: str) -> tuple[bool, str]:
"""
校验财务大写金额是否规范合规
:param cap_str: 完整大写字符串,例:人民币壹万贰仟叁佰肆拾伍元陆角柒分
:return: (是否合法, 错误说明)
"""
s = cap_str.strip()
if not s:
return false, "输入不能为空"
# 规则1:必须以人民币开头
if not s.startswith("人民币"):
return false, "规范大写必须以「人民币」开头"
# 规则2:过滤非法字符
char_list = list(s)
for char in char_list:
if char not in valid_chars and char not in {"人", "民", "币"}:
return false, f"包含非法字符:{char},仅允许标准财务大写汉字"
# 规则3:整体格式正则校验
if not pattern.match(s):
return false, "金额格式不符合财务规范,单位语序错误(拾无前壹、单位错乱等)"
# 规则4:有分不能带整
if "分" in s and "整" in s:
return false, "含分位的金额末尾不能添加「整」字"
# 规则5:拾位前必须带壹,禁止拾元、拾万
if re.search(r"([零佰仟万亿])拾(元|万|亿)", s):
pass
elif re.search(r"^人民币拾", s) or re.search(r"元拾", s) or re.search(r"万拾", s) or re.search(r"亿拾", s):
return false, "拾位前缺少「壹」,规范应为壹拾元/壹拾万"
# 规则6:禁止连续多个零
if "零零" in s:
return false, "禁止书写连续多个「零」"
return true, "财务大写格式规范"
# ------------------- 测试入口 -------------------
if __name__ == "__main__":
print("===== 大写转数字测试 =====")
print(get_payment_fix("负壹万贰仟叁佰肆拾伍元陆角柒分")) # -12345.67
print(get_payment_fix("壹拾万零捌佰元整")) # 100800.0
print(get_payment_fix("叁拾元贰角")) # 30.2
print(get_payment_fix("伍分")) # 0.05
print(get_payment_fix("零元捌分")) # 0.08
print(get_payment_fix("贰亿零伍万元整")) # 200050000.0
print("\n===== 财务大写规范校验测试 =====")
test_cases = [
"人民币壹佰元整", # 合法
"人民币壹拾贰元叁角肆分", # 合法
"人民币拾元整", # 非法:缺壹
"人民币壹佰零拾元整", # 非法:零拾
"人民币壹佰元叁角贰分整", # 非法:有分带整
"人民币二佰元整", # 非法:简体二
"人民币100元整", # 非法:阿拉伯数字
"人民币壹万零零伍元整" # 非法:连续零
]
for case in test_cases:
res, msg = check_fin_capital(case)
print(f"{case} → {res} | {msg}")
四、核心模块深度解析
1. 全角转半角函数 q2b / stringq2b
业务场景中ocr识别、扫描件文本经常出现全角汉字、全角空格,会导致匹配、转换失败。原理:
- 全角字符编码区间
0xff00 ~ 0xff5e,减去偏移0xfee0即可转为半角; - 全角空格
0x3000单独映射为半角空格0x0020; - 超出半角可视字符范围的字符保持原样,避免破坏原有文本。
2. 大写转数字核心 cnmoney_to_num
分段逻辑
将金额拆分为整数元、角、分两部分分别计算,角分直接转为小数(0.1、0.01)。
大数(亿/万)计算逻辑(难点)
普通拾、佰、仟是局部单位,万、亿是分段单位,不能简单顺序相乘:
例:贰亿零伍万元 = 2 * 1亿 + 5 * 1万
代码使用 total 存储亿、万级大数总和,tmp 存储万以内临时数值,遇到亿/万时将临时值合并进总和,清空临时缓存,完美解决多级大数叠加错乱问题。
兼容处理
- 「圆」自动替换为「元」;
- 末尾「整」直接剔除;
- 无元只有角分(伍分、叁角零分)兼容;
- 末尾无单位数字(壹拾万零捌佰)兜底处理。
3. 业务封装 get_payment_fix
面向业务系统的顶层函数,解决3个工程痛点:
- 原始文本长、混杂无关文字,通过正则自动截取「大写xxx)」金额片段;
- 识别「负」「调减」两种财务负数场景,自动返回负金额;
- 统一预处理全角字符,上层业务无需额外格式化。
4. 财务大写校验 check_fin_capital
生产环境必备防护,防止错误大写流入计算导致对账差错,覆盖6类高频错误:
- 缺少「人民币」前缀;
- 混入简体数字、阿拉伯数字、特殊符号;
- 单位语序错误(拾无前壹、零拾);
- 分后加「整」;
- 连续多个零;
- 空输入拦截。
五、常见业务坑点与解决方案
| 坑点场景 | 问题现象 | 代码解决方案 |
|---|---|---|
| 文本含全角大写 | 正则匹配失败、转换结果为0 | 前置 stringq2b 统一半角 |
| 金额使用「圆」 | 分割元失败,角分丢失 | replace("圆","元") 兼容 |
| 拾元、拾万简写 | 财务不合规,人工篡改风险 | 校验函数拦截,提示缺少壹 |
| 亿万多级大数叠加 | 普通顺序算法数值翻倍错乱 | 分层缓存 total + tmp 分段计算 |
| 单据标注「调减」 | 需要负金额参与对账 | 业务入口识别flag,结果取反 |
| ocr脏字符、简体字 | 转换抛出异常、金额错误 | 校验模块提前拦截非法字符 |
| 有分又带整 | 财务凭证不规范 | 校验规则直接报错 |
六、落地集成建议
票据ocr场景:ocr返回整张单据文本,直接调用 get_payment_fix 自动抽取并转换金额;转换前可先调用 check_fin_capital 校验大写合法性,非法金额标记异常单据人工复核。
erp财务对账:读取合同、报销单的大写字段,批量转换为数字用于总额核对、差异计算。
数据分析/报表:pandas批量处理文本列,封装apply函数批量转换大写金额:
import pandas as pd df["数字金额"] = df["原始文本"].apply(get_payment_fix)
接口安全防护:对外提供金额转换接口时,先执行check_fin_capital校验,不合法大写直接返回错误提示,避免脏数据计算异常。
七、扩展优化方向(按需改造)
- 增加大写转数字反向函数(数字自动生成标准财务大写);
- 支持更高单位(兆),扩展unit_map字典;
- 增加金额上下限校验,拦截超大异常金额;
- 日志埋点:记录非法大写、负数金额日志,便于财务审计;
- 适配pdf解析、excel读取场景,封装工具类。
结语
本文方案区别于网上简易demo,完全贴合财务真实业务,兼顾文本抽取、格式兼容、大数计算、数据校验四大核心需求,代码无第三方依赖,仅使用python内置re正则库,python3.6+均可直接运行,可快速集成到票据识别、财务系统、数据分析各类项目,解决中文大写金额自动化解析痛点。
以上就是python实现人民币大写金额转数字和大写规范校验的详细内容,更多关于python大写金额转数字的资料请关注代码网其它相关文章!
发表评论