笔记:财务数字转换大小写
写在前面
奇葩的需求总是显得那么无聊,即使以前写过以为分分钟就搞定,奈何没有记录也得重新花几个小时。。。
核心代码
<?php
namespace App\Service\Common;
class NumericToCnUtil
{
/**
* 数字映射配置
*/
public static array $chineseNumbers = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
public static array $financeNumbers = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
/**
* 单位
*/
public static array $integerUnits = ['', '十', '百', '千'];
public static array $financeIntegerUnits = ['', '拾', '佰', '仟'];
public static array $sectionUnits = ['', '万', '亿', '万亿'];
public static array $decimalUnits = ['角', '分', '厘', '毫'];
/**
* 转普通中文数字
*/
public static function toChinese(float|int $number): string
{
if ($number == 0) {
return '零';
}
$integerPart = (int)floor($number);
$decimalPart = abs($number) - $integerPart;
$result = self::convertInteger($integerPart, self::$chineseNumbers, self::$integerUnits);
$decimalStr = self::convertDecimal($decimalPart, self::$chineseNumbers);
// 纯小数场景
if ($result === '' && $decimalStr !== '') {
$result = '零' . $decimalStr;
} else {
$result .= $decimalStr;
}
// "一十"开头的简化
if (str_starts_with($result, '一十')) {
$result = substr($result, 3);
}
return $result;
}
/**
* 转财务标准大写(人民币)
*/
public static function toFinanceChinese(float|int $number): string
{
if ($number == 0) {
return '零元整';
}
$integerPart = (int)floor($number);
$decimalPart = abs($number) - $integerPart;
$integerResult = self::convertInteger($integerPart, self::$financeNumbers, self::$financeIntegerUnits);
$decimalResult = self::convertFinanceDecimal($decimalPart);
// 零元的情况
if ($integerResult === '') {
$result = '零元' . $decimalResult;
} else {
$result = $integerResult . '元' . $decimalResult;
}
// "壹拾"开头的简化
if (str_starts_with($result, '壹拾')) {
$result = substr($result, 3);
}
// 小数部分为空时补"整"字
if ($decimalResult === '') {
$result .= '整';
}
return $result;
}
/**
* 通用整数部分转换
*/
public static function convertInteger(int $number, array $numberMap, array $unitMap): string
{
if ($number == 0) {
return '';
}
$str = (string)$number;
$len = strlen($str);
$result = '';
$lastNonZero = false;
$hasZeroInSection = false;
for ($i = 0; $i < $len; $i++) {
$digit = (int)$str[$i];
$pos = $len - $i - 1;
$unitIndex = $pos % 4;
$sectionIndex = (int)($pos / 4);
if ($digit === 0) {
$hasZeroInSection = true;
continue;
}
// 处理连续零
if ($hasZeroInSection && $lastNonZero) {
$result .= $numberMap[0];
$hasZeroInSection = false;
}
$result .= $numberMap[$digit] . $unitMap[$unitIndex];
$lastNonZero = true;
// 添加节单位
if ($unitIndex === 0) {
$result .= self::$sectionUnits[$sectionIndex];
$hasZeroInSection = false;
}
}
return $result;
}
/**
* 通用小数部分转换:
* 直接返回"点"
* 保留4位小数并移除末尾零
*/
public static function convertDecimal(float $decimal, array $numberMap): string
{
if ($decimal == 0) {
return '';
}
// 保留4位小数并移除末尾零
$decimalStr = rtrim(sprintf('%.4f', $decimal), '0');
$decimalStr = substr($decimalStr, strpos($decimalStr, '.') + 1);
// 直接返回"点"
return '点' . implode('', array_map(
fn($d) => $numberMap[(int)$d],
str_split($decimalStr)
));
}
/**
* 财务专用小数转换(处理角分厘毫)
*/
public static function convertFinanceDecimal(float $decimal): string
{
if ($decimal == 0) {
return '';
}
$decimalStr = rtrim(sprintf('%.4f', $decimal), '0');
$decimalStr = substr($decimalStr, strpos($decimalStr, '.') + 1);
$result = '';
for ($i = 0; $i < min(4, strlen($decimalStr)); $i++) {
$digit = (int)$decimalStr[$i];
if ($digit > 0) {
$result .= self::$financeNumbers[$digit] . self::$decimalUnits[$i];
}
}
return $result;
}
}
其他
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: