最近由于項(xiàng)目的原因,需要寫一個(gè)貨幣數(shù)字轉(zhuǎn)換中文的算法,先在網(wǎng)了找了一下,結(jié)果發(fā)現(xiàn)無一列外都是用(replace)替換的方式來實(shí)現(xiàn)的,所以想寫個(gè)另外的算法;因?yàn)楸救耸菍W(xué)數(shù)學(xué)出身的,所以用純數(shù)學(xué)的方法實(shí)現(xiàn)。
注意:本文中的算法支持小于1023 (也就是9999億兆)貨幣數(shù)字轉(zhuǎn)化。
貨幣中文說明: 在說明代碼之前,首先讓我們回顧一下貨幣的讀法。
10020002.23 讀為 壹仟零貳萬零貳元貳角叁分
1020 讀為 壹仟零貳拾元整。
100000 讀為 拾萬元整
0.13 讀為 壹角叁分
代碼:
測試工程
static void main(string[] args)
{
console.writeline("請輸入金額");
string inputnum = console.readline();
while (inputnum != "exit")
{
//貨幣數(shù)字轉(zhuǎn)化類
numcast nc = new numcast();
if (nc.isvalidated<string>(inputnum))
{
try
{
string chinesecharacter = nc.converttochinese(inputnum);
console.writeline(chinesecharacter);
}
catch (exception er)
{
console.writeline(er.message);
}
}
else
{
console.writeline("不合法的數(shù)字或格式");
}
console.writeline("/n請輸入金額");
inputnum = console.readline();
}
console.readline();
}
測試結(jié)果如下:
貨幣轉(zhuǎn)化類(numcast類)功能介紹
1 常量的規(guī)定
/// <summary>
/// 數(shù)位
/// </summary>
public enum numlevel { cent, chiao, yuan, ten, hundred, thousand, tenthousand, hundredmillon, trillion };
/// <summary>
/// 數(shù)位的指數(shù)
/// </summary>
private int[] numlevelexponent = new int[] { -2, -1, 0, 1, 2, 3, 4, 8, 12 };
/// <summary>
/// 數(shù)位的中文字符
/// </summary>
private string[] numleverchinesesign = new string[] { "分", "角", "元", "拾", "佰", "仟", "萬", "億", "兆" };
/// <summary>
/// 大寫字符
/// </summary>
private string[] numchinesecharacter = new string[] {"零","壹","貳","叁","肆","伍","陸","柒","捌","玖"};
/// <summary>
/// 整(當(dāng)沒有 角分 時(shí))
/// </summary>
private const string endofint = "整";
2:數(shù)字合法性驗(yàn)證,采用正則表達(dá)式驗(yàn)證
/// <summary>
/// 正則表達(dá)驗(yàn)證數(shù)字是否合法
/// </summary>
/// <param name="num"></param>
/// <returns></returns>
public bool isvalidated<t>(t num)
{
regex reg = new regex(@"^(([0])|([1-9]/d{0,23}))(/./d{1,2})?$");
if (reg.ismatch(num.tostring()))
{
return true;
}
return false;
}
3: 獲取數(shù)位 例如 1000的數(shù)位為 numlevel.thousand
/// <summary>
/// 獲取數(shù)字的數(shù)位 使用log
/// </summary>
/// <param name="num"></param>
/// <returns></returns>
private numlevel getnumlevel(double num)
{
double numlevellength;
numlevel nlvl = new numlevel();
if (num > 0)
{
numlevellength = math.floor(math.log10(num));
for (int i = numlevelexponent.length - 1; i >= 0; i--)
{
if (numlevellength >= numlevelexponent[i])
{
nlvl = (numlevel)i;
break;
}
}
}
else
{
nlvl = numlevel.yuan;
}
return nlvl;
}
4:判斷數(shù)字之間是否有跳位,也就是中文中間是否要加零,例如1020 就應(yīng)該加零。
/// <summary>
/// 是否跳位
/// </summary>
/// <returns></returns>
private bool isdumplevel(double num)
{
if (num > 0)
{
numlevel? currentlevel = getnumlevel(num);
numlevel? nextlevel = null;
int numexponent = this.numlevelexponent[(int)currentlevel];
double postfixnun = math.round(num % (math.pow(10, numexponent)),2);
if(postfixnun> 0)
nextlevel = getnumlevel(postfixnun);
if (currentlevel != null && nextlevel != null)
{
if (currentlevel > nextlevel + 1)
{
return true;
}
}
}
return false;
}
5 把長數(shù)字分割為兩個(gè)較小的數(shù)字?jǐn)?shù)組,例如把9999億兆,分割為9999億和0兆,
因?yàn)橛?jì)算機(jī)不支持過長的數(shù)字。
/// <summary>
/// 是否大于兆,如果大于就把字符串分為兩部分,
/// 一部分是兆以前的數(shù)字
/// 另一部分是兆以后的數(shù)字
/// </summary>
/// <param name="num"></param>
/// <returns></returns>
private bool isbigthantillion(string num)
{
bool isbig = false;
if (num.indexof('.') != -1)
{
//如果大于兆
if (num.indexof('.') > numlevelexponent[(int)numlevel.trillion])
{
isbig = true;
}
}
else
{
//如果大于兆
if (num.length > numlevelexponent[(int)numlevel.trillion])
{
isbig = true;
}
}
return isbig;
}
/// <summary>
/// 把數(shù)字字符串由‘兆’分開兩個(gè)
/// </summary>
/// <returns></returns>
private double[] splitnum(string num)
{
//兆的開始位
double[] tillionlevelnums = new double[2];
int trillionlevellength;
if (num.indexof('.') == -1)
trillionlevellength = num.length - numlevelexponent[(int)numlevel.trillion];
else
trillionlevellength = num.indexof('.') - numlevelexponent[(int)numlevel.trillion];
//兆以上的數(shù)字
tillionlevelnums[0] = convert.todouble(num.substring(0, trillionlevellength));
//兆以下的數(shù)字
tillionlevelnums[1] = convert.todouble(num.substring(trillionlevellength ));
return tillionlevelnums;
}
6 是否以“壹拾”開頭,如果是就可以把它變?yōu)椤笆啊?br> bool isstartoften = false;
while (num >=10)
{
if (num == 10)
{
isstartoften = true;
break;
}
//num的數(shù)位
numlevel currentlevel = getnumlevel(num);
int numexponent = this.numlevelexponent[(int)currentlevel];
num = convert.toint32(math.floor(num / math.pow(10, numexponent)));
if (currentlevel == numlevel.ten && num == 1)
{
isstartoften = true;
break;
}
}
return isstartoften;
7 合并大于兆連個(gè)數(shù)組轉(zhuǎn)化成的貨幣字符串
/// <summary>
/// 合并分開的數(shù)組中文貨幣字符
/// </summary>
/// <param name="tillionnums"></param>
/// <returns></returns>
private string contactnumchinese(double[] tillionnums)
{
string uptillionstr = calculatechinesesign(tillionnums[0], numlevel.trillion, true, isstartoften(tillionnums[0]));
string downtrillionstr = calculatechinesesign(tillionnums[1], null, true,false);
string chinesecharactor = string.empty;
//分開后的字符是否有跳位
if (getnumlevel(tillionnums[1] * 10) == numlevel.trillion)
{
chinesecharactor = uptillionstr + numleverchinesesign[(int)numlevel.trillion] + downtrillionstr;
}
else
{
chinesecharactor = uptillionstr + numleverchinesesign[(int)numlevel.trillion];
if (downtrillionstr != "零元整")
{
chinesecharactor += numchinesecharacter[0] + downtrillionstr;
}
else
{
chinesecharactor += "元整";
}
}
return chinesecharactor;
}
8:遞歸計(jì)算貨幣數(shù)字的中文
/// <summary>
/// 計(jì)算中文字符串
/// </summary>
/// <param name="num">數(shù)字</param>
/// <param name="nl">數(shù)位級別 比如1000萬的 數(shù)位級別為萬</param>
/// <param name="isexceptten">是否以‘壹拾’開頭</param>
/// <returns>中文大寫</returns>
public string calculatechinesesign(double num, numlevel? nl ,bool isdump,bool isexceptten)
{
num = math.round(num, 2);
bool isdump = false;
//num的數(shù)位
numlevel? currentlevel = getnumlevel(num);
int numexponent = this.numlevelexponent[(int)currentlevel];
string result = string.empty;
//整除后的結(jié)果
int prefixnum;
//余數(shù) 當(dāng)為小數(shù)的時(shí)候 分子分母各乘100
double postfixnun ;
if (num >= 1)
{
prefixnum = convert.toint32(math.floor(num / math.pow(10, numexponent)));
postfixnun = math.round(num % (math.pow(10, numexponent)), 2);
}
else
{
prefixnum = convert.toint32(math.floor(num*100 / math.pow(10, numexponent+2)));
postfixnun = math.round(num * 100 % (math.pow(10, numexponent + 2)), 2);
postfixnun *= 0.01;
}
if (prefixnum < 10 )
{
//避免以‘壹拾’開頭
if (!(numchinesecharacter[(int)prefixnum] == numchinesecharacter[1]
&& currentlevel == numlevel.ten && isexceptten))
{
result += numchinesecharacter[(int)prefixnum];
}
else
{
isexceptten = false;
}
//加上單位
if (currentlevel == numlevel.yuan )
{
////當(dāng)為 “元” 位不為零時(shí) 加“元”。
if (nl == null)
{
result += numleverchinesesign[(int)currentlevel];
//當(dāng)小數(shù)點(diǎn)后為零時(shí) 加 "整"
if (postfixnun == 0)
{
result += endofint;
}
}
}
else
{
result += numleverchinesesign[(int)currentlevel];
}
//當(dāng)真正的個(gè)位為零時(shí) 加上“元”
if (nl == null && postfixnun < 1 && currentlevel > numlevel.yuan && postfixnun > 0)
{
result += numleverchinesesign[(int)numlevel.yuan];
}
}
else
{
//當(dāng) 前綴數(shù)字未被除盡時(shí), 遞歸下去
numlevel? nextnl = null;
if ((int)currentlevel >= (int)(numlevel.tenthousand))
nextnl = currentlevel;
result += calculatechinesesign((double)prefixnum, nextnl, isdump, isexceptten);
if ((int)currentlevel >= (int)(numlevel.tenthousand))
{
result += numleverchinesesign[(int)currentlevel];
}
}
//是否跳位
// 判斷是否加零, 比如302 就要給三百 后面加零,變?yōu)?三百零二。
if (isdumplevel(num))
{
result += numchinesecharacter[0];
isdump = true;
}
//余數(shù)是否需要遞歸
if (postfixnun > 0)
{
result += calculatechinesesign(postfixnun, nl, isdump, false);
}
else if (postfixnun == 0 && currentlevel > numlevel.yuan )
{
//當(dāng)數(shù)字是以零元結(jié)尾的加上 元整 比如1000000一百萬元整
if (nl == null)
{
result += numleverchinesesign[(int)numlevel.yuan];
result += endofint;
}
}
return result;
}
9:外部調(diào)用的轉(zhuǎn)換方法。
/// <summary>
/// 外部調(diào)用的轉(zhuǎn)換方法
/// </summary>
/// <param name="num"></param>
/// <returns></returns>
public string converttochinese(string num)
{
if (!isvalidated<string>(num))
{
throw new overflowexception("數(shù)值格式不正確,請輸入小于9999億兆的數(shù)字且最多精確的分的金額!");
}
string chinesecharactor = string.empty;
if (isbigthantillion(num))
{
double[] tillionnums = splitnum(num);
chinesecharactor = contactnumchinese(tillionnums);
}
else
{
double dnum = convert.todouble(num);
chinesecharactor = calculatechinesesign(dnum, null, true, isstartoften(dnum));
}
return chinesecharactor;
}
小結(jié):
個(gè)人認(rèn)為程序的靈魂是算法,大到一個(gè)系統(tǒng)中的業(yè)務(wù)邏輯,小到一個(gè)貨幣數(shù)字轉(zhuǎn)中文的算法,處處都體現(xiàn)一種邏輯思想。
是否能把需求抽象成一個(gè)好的數(shù)學(xué)模型,直接關(guān)系到程序的實(shí)現(xiàn)的復(fù)雜度和穩(wěn)定性。在一些常用功能中想些不一樣的算法,對我們開拓思路很有幫助。
新聞熱點(diǎn)
疑難解答
圖片精選