Files

103 lines
3.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""中文大写金额与阿拉伯金额互转(发票大小写金额一致性校验用)。
支持 "壹仟陆佰伍拾元整"、"壹佰贰拾叁元肆角伍分"、"拾元整" 等常见发票大写写法;
同时兼容 "¥1,234.50" / "1234.50元" 等阿拉伯写法的清洗。
解析失败返回 None,由调用方降级为"待确认",不做硬性驳回。
"""
from __future__ import annotations
import re
from typing import Optional
_DIGITS = {
"零": 0,
"壹": 1,
"贰": 2,
"叁": 3,
"肆": 4,
"伍": 5,
"陆": 6,
"柒": 7,
"捌": 8,
"玖": 9,
}
_SMALL_UNITS = {"拾": 10, "佰": 100, "仟": 1000}
_GROUP_UNITS = {"万": 10**4, "亿": 10**8}
_ALL_KEYS = r"零壹贰叁肆伍陆柒捌玖拾佰仟万亿元圆角分整"
_TRAILING_CHARS = re.compile(r"[人民币¥¥\s,,元圆整]|人民币$")
def clean_amount_text(text: str) -> str:
"""清洗阿拉伯金额文本,返回可 float() 的字符串(失败返回原文本去掉常见缀符)。"""
cleaned = _TRAILING_CHARS.sub("", (text or "").strip())
return cleaned
def parse_cny_upper(text: str) -> Optional[float]:
"""解析中文大写金额为数值;无法解析时返回 None。"""
if not text:
return None
raw = (text or "").strip()
raw = raw.replace("人民币", "").replace("¥", "").replace("¥", "").strip()
raw = re.sub(r"[\s,,]", "", raw)
if not raw:
return None
if re.search(r"[0-9]", raw):
return None # 纯阿拉伯数字写法不属于大写金额
if not re.fullmatch(rf"[{_ALL_KEYS}]+", raw):
return None
int_part, frac_part = raw, ""
for sep in ("元", "圆"):
if sep in raw:
int_part, frac_part = raw.split(sep, 1)
break
try:
yuan = _parse_integer_section(int_part) if int_part else 0.0
except ValueError:
return None
jiao = fen = 0
m_jiao = re.search(r"([零壹贰叁肆伍陆柒捌玖])角", frac_part)
if m_jiao:
jiao = _DIGITS[m_jiao.group(1)]
m_fen = re.search(r"([零壹贰叁肆伍陆柒捌玖])分", frac_part)
if m_fen:
fen = _DIGITS[m_fen.group(1)]
return round(yuan + jiao / 10 + fen / 100, 2)
def _parse_integer_section(section: str) -> float:
"""解析整数段(元之前的部分),如 壹仟陆佰伍拾 / 拾贰万零壹佰。"""
if not section:
return 0.0
if section == "零":
return 0.0
total = 0.0
group_value = 0
number = 0
for ch in section:
if ch in _DIGITS:
number = _DIGITS[ch]
elif ch in _SMALL_UNITS:
unit = _SMALL_UNITS[ch]
group_value += (number if number else 1) * unit
number = 0
elif ch in _GROUP_UNITS:
unit = _GROUP_UNITS[ch]
group_value = (group_value + number) * unit
total += group_value
group_value = 0
number = 0
else:
raise ValueError(f"非法大写字符: {ch}")
total += group_value + number
if total <= 0:
raise ValueError("大写金额解析为 0")
return total