笔记:财务数字转换大小写

写在前面

奇葩的需求总是显得那么无聊,即使以前写过以为分分钟就搞定,奈何没有记录也得重新花几个小时。。。

核心代码

<?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 协议》,转载必须注明作者和本文链接
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
讨论数量: 4
Mutoulee

大才!~

10小时前 评论
游离不2

看看这个版本怎么样

<?php

/**
 * 数字转财务大写
 *
 * @param float|int|string $number 输入金额(支持整数、小数)
 * @return string
 */
function numberToChineseMoney($number): string
{
    $cnNums    = ["零","壹","贰","叁","肆","伍","陆","柒","捌","玖"];
    $cnIntRadice = ["","拾","佰","仟"];
    $cnIntUnits  = ["","万","亿","兆"];
    $cnDecUnits  = ["角","分"];
    $cnInteger   = "整";
    $cnIntLast   = "元";
    $maxNum      = 999999999999999.99;

    $number = round($number, 2); // 保留两位小数
    if ($number > $maxNum) {
        return "超出最大处理数字";
    }

    $integerNum = floor($number); // 整数部分
    $decimalNum = round(($number - $integerNum) * 100); // 小数部分(两位)

    $output = "";

    // 处理整数部分
    if ($integerNum > 0) {
        $zeroCount = 0;
        $intLen = strlen((string)$integerNum);
        for ($i = 0; $i < $intLen; $i++) {
            $n = substr((string)$integerNum, $i, 1);
            $p = $intLen - $i - 1; // 余数位置
            $q = intval($p / 4);  // 单位:万、亿、兆
            $m = $p % 4;          // 个十百千
            if ($n == "0") {
                $zeroCount++;
            } else {
                if ($zeroCount > 0) {
                    $output .= $cnNums[0];
                }
                $zeroCount = 0;
                $output .= $cnNums[$n] . $cnIntRadice[$m];
            }
            if ($m == 0 && $zeroCount < 4) {
                $output .= $cnIntUnits[$q];
            }
        }
        $output .= $cnIntLast;
    }

    // 处理小数部分
    if ($decimalNum > 0) {
        $jiao = intval($decimalNum / 10);
        $fen  = $decimalNum % 10;
        if ($jiao > 0) {
            $output .= $cnNums[$jiao] . $cnDecUnits[0];
        }
        if ($fen > 0) {
            $output .= $cnNums[$fen] . $cnDecUnits[1];
        }
    } else {
        $output .= $cnInteger;
    }

    return $output;
}

// 示例
echo numberToChineseMoney(1234.56); 
// 输出:壹仟贰佰叁拾肆元伍角陆分

echo numberToChineseMoney(100010001.01);
// 输出:壹亿零壹万零壹元零壹分
10小时前 评论

这玩意丢给Ai,不是一分钟不到就出来了?

9小时前 评论
梦想星辰大海

// ToCnCurrency 将金额转换为中文大写形式
func ToCnCurrency(amount float64) string {
    if amount == 0 {
        return "零元整"
    }
    // 定义数字对应的大写字符
    cnNums := []string{"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"}
    // 定义整数部分的单位
    cnIntUnits := []string{"", "拾", "佰", "仟"}
    cnBigUnits := []string{"", "万", "亿", "兆"} // 支持更大单位(可选)
    // 定义小数部分的单位
    cnDecUnits := []string{"角", "分"}

    // 处理负数
    sign := ""
    if amount < 0 {
        sign = "负"
        amount = -amount
    }

    // 转换为字符串,避免浮点数精度问题
    numStr := fmt.Sprintf("%.2f", math.Round(amount*100)/100)
    parts := strings.Split(numStr, ".")
    integerPart := parts[0]
    decimalPart := parts[1]

    // 处理整数部分
    integerStr := ""
    zeroCount := 0
    intLen := len(integerPart)

    for i := 0; i < intLen; i++ {
        n := string(integerPart[i])
        unitPos := (intLen - i - 1) % 4    // 当前位的单位(仟/佰/拾)
        bigUnitPos := (intLen - i - 1) / 4 // 万/亿等大单位

        if n == "0" {
            zeroCount++
        } else {
            if zeroCount > 0 {
                integerStr += cnNums[0]
            }
            integerStr += cnNums[n[0]-'0'] + cnIntUnits[unitPos]
            zeroCount = 0
        }

        // 处理大单位(万、亿)
        if unitPos == 0 && zeroCount < 4 {
            integerStr += cnBigUnits[bigUnitPos]
        }
    }

    // 处理小数部分
    decimalStr := ""
    for i := 0; i < len(decimalPart); i++ {
        n := string(decimalPart[i])
        if n != "0" {
            decimalStr += cnNums[n[0]-'0'] + cnDecUnits[i]
        }
    }

    // 组合结果
    result := sign
    if integerStr != "" {
        result += integerStr + "元"
    }
    if decimalStr != "" {
        result += decimalStr
    } else {
        result += "整"
    }

    return result
}

让ai写

8小时前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!