zoukankan      html  css  js  c++  java
  • 利用结构化的设计看待人民币大写转换

    在网上看到不少人民币大写转换的类,或多或少的总有一些问题,不放心用在企业开发上。而且每个人的思路总是天马行空,别说面向对象的方法,就是面向过程也说不通,充斥着各种各样的特殊情况。如果少考虑一种情况,那结果就很难预料,而且代码的可读性,我实在不敢恭维。

    昨天,突然心血来潮,用结构化的思想整理了一遍,在这里就拿出来分享一下。

    首先,数字应该分段,分成万以下的、万、亿、兆这几个段,并且每个段的长度固定是4个数字。比如说 123456 分为两个段,前面一个未满的段:12 ,后面是满的段:3456 。

    其次,每个满段其实都是个十百千四个单位。比如:12345678 ,转成数字大写是:壹仟贰佰叁拾肆  万  伍仟陆佰柒拾捌,发现什么规律了吗?

    最后,就是处理一些必须有 0 的问题了,比如说:303 ,不能说:叁佰叁,应该是:叁佰零叁,这个“零”有很多讲究的。

    针对这些规律,我有针对性的整理出来两个方法:SingleConvert 、MultiConvert 。其中 SingleConvert 处理段内的逻辑,MultiConvert 处理段间的逻辑,在段内的数字 0 问题穿插到 SingleConvert 方法中,段间的数字 0 问题穿插到 MultiConvert 方法中。另外,小数部分的处理,只有两位数字,可以写死,这个最简单了。

    说一下数字 0 的逻辑。

    数字【3003】转换出来应该是【叁仟零叁】,数字【303】转换出来应该是【叁佰零叁】,可以看到段内连续出现一个或多个数字 0 的情况下,只会出现一个汉字【零】。

    数字【3000000000300】转换出来应该是【叁兆零叁佰】,数字【300000300】转换出来应该是【叁亿零叁佰】,可以看到段间连续出现一个或多个数字 0 的情况下,只会出现一个汉字【零】。

    所以,段内逻辑和段间逻辑,在连续出现多个【零】的时候,只保留一个【零】

    数字【300】转换出来应该是【叁佰】,数字【3000】转换出来应该是【叁仟】,可以看到在段内最后不管连续出现多少【零】,都抹掉不提。

    所以,段内逻辑中,末尾的一个或多个【零】,均不保留

    还有两个不值得提的规律:每个数字对应一个单位,必定是【个十百千】;每段对应一个单位,从低到高的对应是【空、万、亿、兆、……】

    原谅我不知大更高的单位是什么了,这辈子没见过那么多钱,神那~~

    然后,然后就没规律了,下面上代码:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
     
    namespace Lenic.Core
    {
        /// <summary>
        /// 数字转大写汉字字符类
        /// </summary>
        [DebuggerStepThrough]
        public class NumericConvert
        {
            #region Private Fields
            /// <summary>
            /// 段的分隔字符,从低到高依次增加:空、万、亿、万亿,最高不超过万亿的数字都可以
            /// </summary>
            public static readonly string[] DefaultRangeNumeric = new string[] { string.Empty, "万", "亿", "兆" };
            /// <summary>
            /// 位的分隔字符,从低到高依次是:仟、佰、拾、空
            /// </summary>
            public static readonly char[] DefaultUnitNumeric = new char[] { '仟', '佰', '拾', char.MinValue };
            /// <summary>
            /// 数字替换的字符,从低到高依次是:零、壹、贰、叁、肆、伍、陆、柒、捌、玖
            /// </summary>
            public static readonly char[] DefaultCharNumeric = new char[] { '零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖' };
     
            private char[] charNumeric = DefaultCharNumeric;
            private string[] rangeNumeric = DefaultRangeNumeric;
            private char zeroNumeric = DefaultCharNumeric[0];
            private char[] unitNumeric = DefaultUnitNumeric;
            #endregion
     
            #region Business Methods
            /// <summary>
            /// 重置数字替换的字符,必须从小到大是 10 个汉字字符。
            /// </summary>
            /// <param name="data">目标字符数组</param>
            /// <returns>成功替换则返回 <c>true</c> 。</returns>
            public bool ResetCharNumeric(char[] data)
            {
                if (data == null || data.Length != 10)
                    return false;
     
                charNumeric = data;
                zeroNumeric = data[0];
                return true;
            }
     
            /// <summary>
            /// 重置位的分隔字符,必须从小到大是 4 个汉字字符。
            /// </summary>
            /// <param name="data">目标字符数组</param>
            /// <returns>成功替换则返回 <c>true</c> 。</returns>
            public bool ResetUnitNumeric(char[] data)
            {
                if (data == null || data.Length != 4)
                    return false;
     
                unitNumeric = data;
                return true;
            }
     
            /// <summary>
            /// 重置段的分隔字符。
            /// </summary>
            /// <param name="data">目标字符数组</param>
            public void ResetRangeNumeric(string[] data)
            {
                rangeNumeric = data ?? DefaultRangeNumeric;
            }
     
            /// <summary>
            /// 执行数字转大写汉字字符的操作。
            /// </summary>
            /// <param name="obj">待转换的数字</param>
            /// <returns>转换完成的大写汉字字符串。</returns>
            public string Convert(decimal obj)
            {
                if (obj > 9999999999999999.99M)
                    throw new ApplicationException("The numeric too big!");
     
                var data = obj.ToString("#.##");
     
                var list = data.Split('.');
     
                var result = MultiConvert(list[0]);
                if (list.Length > 1)
                    result += DecimalConvert(list[1]);
     
                return result;
            }
            #endregion
     
            #region Private Methods
            private string MultiConvert(string data)
            {
                var list = Split(data).ToArray();
     
                var results = new List<string>();
                foreach (var item in list)
                    results.Add(SingleConvert(item));
     
                var sbResult = new StringBuilder();
                var len = results.Count;
                var index = len - 1;
     
                for (int i = 0; i < len; i++)
                {
                    var item = results[i];
                    if ((i + 2 < len) && item == zeroNumeric.ToString() && results[i + 1].StartsWith(zeroNumeric.ToString()))
                        continue;
     
                    if (!(i == (len - 1) && item == zeroNumeric.ToString()))
                        sbResult.Append(item);
     
                    var unit = rangeNumeric[index - i];
                    if (unit != string.Empty && item != zeroNumeric.ToString())
                        sbResult.Append(unit);
                }
     
                if (sbResult[sbResult.Length - 1] == zeroNumeric)
                    sbResult.Remove(sbResult.Length - 1, 1);
                sbResult.Append("元");
     
                return sbResult.ToString();
            }
     
            private string SingleConvert(string data)
            {
                var len = data.Length;
                var result = new List<char>();
                var previousChar = char.MinValue;
                var unitIndex = len == 4 ? 0 : (4 - len);
     
                for (int i = 0; i < len; i++)
                {
                    var item = CharToInt(data[i]);
                    var currentChineseChar = charNumeric[item];
                    if (currentChineseChar == previousChar && previousChar == zeroNumeric && currentChineseChar == zeroNumeric)
                        continue;
                    else
                    {
                        result.Add(previousChar = currentChineseChar);
     
                        var currentUnit = unitNumeric[unitIndex + i];
                        if (currentChineseChar != zeroNumeric && currentUnit != char.MinValue)
                            result.Add(currentUnit);
                    }
                }
     
                if (result.Count != 1 && result.Last() == zeroNumeric)
                    result.RemoveAt(result.Count - 1);
     
                return new string(result.ToArray());
            }
     
            private string DecimalConvert(string data)
            {
                StringBuilder sbResult = new StringBuilder();
                if (data[0] != '0')
                {
                    sbResult.Append(charNumeric[CharToInt(data[0])]);
                    sbResult.Append("角");
                }
                if (data[1] != '0')
                {
                    sbResult.Append(charNumeric[CharToInt(data[1])]);
                    sbResult.Append("分");
                }
                return sbResult.ToString();
            }
     
            private IEnumerable<string> Split(string data)
            {
                var len = data.Length / 4;
                var mod = data.Length % 4;
                if (mod != 0)
                    len += 1;
     
                var startIndex = 0;
                var blockLength = mod != 0 ? mod : 4;
     
                for (int i = 0; i < len; i++)
                {
                    yield return data.Substring(startIndex, blockLength);
                    startIndex += blockLength;
                    blockLength = 4;
                }
            }
     
            private int CharToInt(char obj)
            {
                return ((int)obj) - 48;
            }
            #endregion
        }
    }

    如果你喜欢本文,请点一下推荐,谢谢!

  • 相关阅读:
    在Java中使用 break/continue 语句来控制多重嵌套循环的跳转
    Android系统架构基本模式解析
    添加蓝牙通讯功能
    wince串口蓝牙
    Android各层推荐开发书籍及参考资料
    CDEFINES的用法
    蓝牙地址的规则
    WinCE中sources文件中targetlibs与sourcelibs的作用与区别
    Java初学者不得不知的概念,JDK,JRE,JVM的区别?
    WINCE的内存配置config.bib文件的解析
  • 原文地址:https://www.cnblogs.com/lenic/p/2626977.html
Copyright © 2011-2022 走看看