利用結(jié)構(gòu)化的設(shè)計(jì)看待人民幣大寫(xiě)轉(zhuǎn)換
昨天,突然心血來(lái)潮,用結(jié)構(gòu)化的思想整理了一遍,在這里就拿出來(lái)分享一下。
首先,數(shù)字應(yīng)該分段,分成萬(wàn)以下的、萬(wàn)、億、兆這幾個(gè)段,并且每個(gè)段的長(zhǎng)度固定是4個(gè)數(shù)字。比如說(shuō) 123456 分為兩個(gè)段,前面一個(gè)未滿的段:12 ,后面是滿的段:3456 。
其次,每個(gè)滿段其實(shí)都是個(gè)十百千四個(gè)單位。比如:12345678 ,轉(zhuǎn)成數(shù)字大寫(xiě)是:壹仟貳佰叁拾肆 萬(wàn) 伍仟陸佰柒拾捌,發(fā)現(xiàn)什么規(guī)律了嗎?
***,就是處理一些必須有 0 的問(wèn)題了,比如說(shuō):303 ,不能說(shuō):叁佰叁,應(yīng)該是:叁佰零叁,這個(gè)“零”有很多講究的。
針對(duì)這些規(guī)律,我有針對(duì)性的整理出來(lái)兩個(gè)方法:SingleConvert 、MultiConvert 。其中 SingleConvert 處理段內(nèi)的邏輯,MultiConvert 處理段間的邏輯,在段內(nèi)的數(shù)字 0 問(wèn)題穿插到 SingleConvert 方法中,段間的數(shù)字 0 問(wèn)題穿插到 MultiConvert 方法中。另外,小數(shù)部分的處理,只有兩位數(shù)字,可以寫(xiě)死,這個(gè)最簡(jiǎn)單了。
說(shuō)一下數(shù)字 0 的邏輯。
數(shù)字【3003】轉(zhuǎn)換出來(lái)應(yīng)該是【叁仟零叁】,數(shù)字【303】轉(zhuǎn)換出來(lái)應(yīng)該是【叁佰零叁】,可以看到段內(nèi)連續(xù)出現(xiàn)一個(gè)或多個(gè)數(shù)字 0 的情況下,只會(huì)出現(xiàn)一個(gè)漢字【零】。
數(shù)字【3000000000300】轉(zhuǎn)換出來(lái)應(yīng)該是【叁兆零叁佰】,數(shù)字【300000300】轉(zhuǎn)換出來(lái)應(yīng)該是【叁億零叁佰】,可以看到段間連續(xù)出現(xiàn)一個(gè)或多個(gè)數(shù)字 0 的情況下,只會(huì)出現(xiàn)一個(gè)漢字【零】。
所以,段內(nèi)邏輯和段間邏輯,在連續(xù)出現(xiàn)多個(gè)【零】的時(shí)候,只保留一個(gè)【零】。
數(shù)字【300】轉(zhuǎn)換出來(lái)應(yīng)該是【叁佰】,數(shù)字【3000】轉(zhuǎn)換出來(lái)應(yīng)該是【叁仟】,可以看到在段內(nèi)***不管連續(xù)出現(xiàn)多少【零】,都抹掉不提。所以,段內(nèi)邏輯中,末尾的一個(gè)或多個(gè)【零】,均不保留。
還有兩個(gè)不值得提的規(guī)律:每個(gè)數(shù)字對(duì)應(yīng)一個(gè)單位,必定是【個(gè)十百千】;每段對(duì)應(yīng)一個(gè)單位,從低到高的對(duì)應(yīng)是【空、萬(wàn)、億、兆、……】。原諒我不知大更高的單位是什么了,這輩子沒(méi)見(jiàn)過(guò)那么多錢,神那~~
然后,然后就沒(méi)規(guī)律了,下面上代碼:
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Text;
- namespace Lenic.Core
- {
- /// <summary>
- /// 數(shù)字轉(zhuǎn)大寫(xiě)漢字字符類
- /// </summary>
- [DebuggerStepThrough]
- public class NumericConvert
- {
- #region Private Fields
- /// <summary>
- /// 段的分隔字符,從低到高依次增加:空、萬(wàn)、億、萬(wàn)億,***不超過(guò)萬(wàn)億的數(shù)字都可以
- /// </summary>
- public static readonly string[] DefaultRangeNumeric = new string[] { string.Empty, "萬(wàn)", "億", "兆" };
- /// <summary>
- /// 位的分隔字符,從低到高依次是:仟、佰、拾、空
- /// </summary>
- public static readonly char[] DefaultUnitNumeric = new char[] { '仟', '佰', '拾', char.MinValue };
- /// <summary>
- /// 數(shù)字替換的字符,從低到高依次是:零、壹、貳、叁、肆、伍、陸、柒、捌、玖
- /// </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>
- /// 重置數(shù)字替換的字符,必須從小到大是 10 個(gè)漢字字符。
- /// </summary>
- /// <param name="data">目標(biāo)字符數(shù)組</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 個(gè)漢字字符。
- /// </summary>
- /// <param name="data">目標(biāo)字符數(shù)組</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">目標(biāo)字符數(shù)組</param>
- public void ResetRangeNumeric(string[] data)
- {
- rangeNumeric = data ?? DefaultRangeNumeric;
- }
- /// <summary>
- /// 執(zhí)行數(shù)字轉(zhuǎn)大寫(xiě)漢字字符的操作。
- /// </summary>
- /// <param name="obj">待轉(zhuǎn)換的數(shù)字</param>
- /// <returns>轉(zhuǎn)換完成的大寫(xiě)漢字字符串。</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
- }
- }