[代码片段]在 C# 中设计一个小写货币数字转中文大写的代码

1 前言

好久不见,最近调岗了,调到了供应链,从明天开始要负责种植场的工作了。但在调来供应链的这段时间里,付款申请的业务实在是有点繁忙,所以就在筹划着是不是可以用技术来解决一下。在这个需求下,其中有一个所有的付款申请都会出现的功能 —— 小写货币数字转中文大写。

示例:

1234567.89 转换为壹佰贰拾叁万肆仟伍佰陆拾柒元捌角玖分整

今天抽了点时间出来写了一下,记录一下设计思路,防止忘记。

2 开发环境

信息 说明
操作系统 Microsoft Windows 11 专业工作站版
开发工具 Microsoft Visual Studio Community 2019

3 思路逻辑

每次写代码想逻辑想不下去的时候,总会告诉自己:“用自己最朴素的想法去思考,如果是人工来判断,会怎么处理?” 我们都知道,中文大写数字的写法,实际上就是数字的读法。因此,从数字的读法出发,发现规律如下:

[代码片段]在 C# 中设计一个小写货币数字转中文大写的代码_第1张图片

如上图,我们可以发现,读数字的时候每 4 个一组,每组有 1 个单位名称,即我们常说的:万、亿。一般公司里的付款申请多数都是万级的,鲜有少数的十万、百万,超过的都是极少的,但写代码需要尽可能考虑得全面一些,因此 Yogurt 特地去找了超过“亿”的单位名称。

[代码片段]在 C# 中设计一个小写货币数字转中文大写的代码_第2张图片

早在一千五百多年前,我国出现了一本数学著作,名曰《孙子算经》,里面记载了万以上的单位名称,Yogurt 整理出来便是如上图所示。恰好也是每万倍一个名称,对上了上面自己找到的 “4 个一组” 的规律。不得不佩服我们的老祖宗在千年以前定好的逻辑一直可以沿用至今。

在读数的过程中,还发现了在一些特殊情况下,读法是有不同的。例如上述绿色区域的 0000,按字面来说,应该读作 零零零零兆,但实际上我们往往就直接读作 了;红色区域的 4050,按字面来说,应该读作 四仟零佰五拾零 的,虽然有点反人类,但字面上应该是这么读的,而实际上我们会读作 肆仟零伍拾;同样的情况也出现在蓝色区域中的 2003 中。简单整理一下这个规律就是:① 一组数字中出现 4 个 时,该组数字读 ,无需读单位;② 一组数字中出现连续的 时,只读作一个 ;③ 当小单位数字为 时,无需读 以及它的单位。

OK,整理一下上述的所有规律如下:

  1. 整数部分的位数从小到大排列,每 4 个一组,每组数字均遵循 “个十百千” 的顺序排列;小数部分固定为 “角分”,为 时不读数字及单位
  2. 整数部分连续出现 时,只读作一个 ,且无需读出单位
  3. 整数部分的小单位数字为 时,无需读 以及它的单位

4 代码编写

搞清楚原理和规律以后,就可以编写代码了。具体过程不再赘述,大家看代码吧

/*
 * @Author: Yogurt_cry
 * @Date: 2023-07-18 14:27:02
 * @LastEditors: Yogurt_cry
 * @LastEditTime: 2023-07-19 18:59:30
 * @Description: 小写货币数字转换中文大写
 * @FilePath: \WechatClientc:\Users\Yogurt_cry\Desktop\Untitled-1.svg
 */

using System;
using System.Collections.Generic;
using System.Text;

namespace PersonalTool
{
    public class NumConvert
    {
        /// 
        /// 反转字符串
        /// 
        /// 
        /// 
        public static string StrRervse(string value)
        {
            char[] valueRervse = value.ToCharArray();
            Array.Reverse(valueRervse);
            return new string(valueRervse);
        }

        private static readonly string NUM_NAME = "零壹贰叁肆伍陆柒捌玖";

        /// 
        /// 数字转换中文货币大写
        /// 
        /// 
        /// 
        public static string ConvertCNYNum(string value)
        {
            // 出现异常直接返回空值
            try
            {
                // 由于数字精度问题无法通过运算解决, 因此使用字符串
                string[] valueSplit = value.Split('.');
                string intPart = __CNNumIntPart__(StrRervse(valueSplit[0]));

                // 整理清除多余的 "零"
                while (intPart.Contains("零零")) intPart = intPart.Replace("零零", "零");
                string decPart = __CNNumDecPart__(valueSplit.Length > 1 ? valueSplit[1] : "");

                return string.Format("{0}元{1}整", intPart, decPart);
            }
            catch { return ""; }
            
        }

        /// 
        /// 转换整数部分
        /// 
        /// 
        /// 
        private static string __CNNumIntPart__(string value)
        {
            string[] groupUnit = new string[] {
                "", "万", "亿", "兆", "京", "垓", "秭", "穰", "沟", "涧", "正", "载", "极"
            };

            List<string> result = new List<string>();
            int groupSize = value.Length / 4;
            for (int i = 0; i <= groupSize; i++)
            {
                int size = i == groupSize ? value.Length % 4 : 4;
                string cnNumGroup4 = __CNNumGroup4__(value.Substring(i * 4, size));
                if (string.IsNullOrWhiteSpace(cnNumGroup4)) result.Add("零");
                else result.Add(cnNumGroup4 + groupUnit[i]);
            }

            result.Reverse();

            return string.Join("", result);
        }

        /// 
        /// 分组处理大写转换
        /// 
        /// 
        /// 
        private static string __CNNumGroup4__(string value)
        {
            string[] wordNum = new string[] { "", "拾", "佰", "仟" };

            string numWithout0 = int.Parse(value).ToString();          // 处理非必要零值
            numWithout0 = numWithout0 == "0" ? "" : numWithout0;

            int wdIndex = value.Length - numWithout0.ToString().Length;
            string lstNum = "";
            List<string> result = new List<string>();
            foreach (char item in numWithout0)
            {
                string num = item.ToString();
                string numWithUnit = "";
                if (num == "0") { if (lstNum != "0") numWithUnit = "零"; }
                else numWithUnit = NUM_NAME[int.Parse(num)].ToString() + wordNum[wdIndex];

                result.Add(numWithUnit);

                lstNum = num;
                wdIndex += 1;
            }

            result.Reverse();

            return string.Join("", result);
        }

        /// 
        /// 转换小数部分
        /// 
        /// 
        /// 
        private static string __CNNumDecPart__(string value)
        {
            List<string> result = new List<string>();
            if (value != "00")
            {
                string __value = value.Length < 2 ? value : value.Substring(0, 2);
                string numUnit = "角分";
                for (int i = 0; i < __value.Length; i++)
                {
                    string num = __value[i].ToString();
                    num = NUM_NAME[int.Parse(num)].ToString();
                    if (num == "零") result.Add(num);
                    else result.Add(num + numUnit[i].ToString());
                }

                if (result.Last() == "零") result.RemoveAt(result.Count - 1);
            }
            return string.Join("", result);
        }
    }
}

5 后记

此前一直负责的是开发工作,接需求做项目,现在直接调岗到业务部门,才发现很多需求并不是不存在,而是业务部门要么是压根并不认为是可以优化的,要么就是不知道居然还有优化的方法,要么就是就算觉得麻烦却不知道上哪去找人优化。这次深入业务部门,自己接手了一些常规工作,才发现好多可以优化的地方。慢慢来吧,一个个优化起来,这样推文也可以有素材更新了,哈哈哈哈哈哈。

你可能感兴趣的:(C#,一些想法,c#,开发语言)