.NET 工具函式 - 阿拉伯數字與中文數字雙向轉換
0 | 2,715 |
十四年前我寫過地址阿拉伯數字轉中文大寫的 .NET 函式,但它有兩個問題:一是依賴 Visual Studio International Feature Pack,二是當年只做了阿拉伯數字轉中文,沒有中文數字逆解回阿拉伯數字的能力。
總之,十四年後我把這個題目當成暖身練習,打算不靠第三方程式庫,用 C# 從頭寫一個阿拉伯數字與中文數字雙向轉換函式。
為了充分測試各種狀況,我寫了一小段程式列舉從零到「1234 兆 1234 億 1234 萬 1234」的 164 = 65536 種組合,將數字轉中文,再將中文轉回數字,並比對還原後數字是否相符。
var numSets = new[] {
"0000", "0001", "0010", "0012",
"0100", "0102", "0310", "0123",
"1000", "1002", "1020", "1023",
"1200", "1203", "1230", "1234"
};
// 列出所有可能的組合
var count = numSets.Length;
var idxs = new int[4];
var lv = idxs.Length - 1;
while (true)
{
var sb = new StringBuilder();
for (var i = 0; i < idxs.Length; i++)
{
sb.Append(numSets[idxs[i]]);
}
long n = long.Parse(sb.ToString());
var cht = ChtNumConverter.ToChtNum(n);
var restored = ChtNumConverter.ParseChtNum(cht);
Console.WriteLine($"{n, -16:n0} {restored, -16:n0} " +
$"\x1b[{(n == restored ? "32mPASS" : "31mFAIL")}\x1b[0m {cht} ");
if (n != restored)
throw new ApplicationException($"數字轉換錯誤 {n} vs {restored}");
if (idxs.All(o => o == count - 1)) break;
idxs[lv]++;
while (idxs[lv] == count)
{
idxs[lv] = 0;
lv--;
if (lv < 0) break;
idxs[lv]++;
}
lv = idxs.Length - 1;
}
隨意抽樣幾個較易出狀況的情境看起來都 OK:
100,102 100,102 十萬零一百零二
1,021,023 1,021,023 一百零二萬一千零二十三
12,000,012 12,000,012 一千二百萬零一十二
103,100,310 103,100,310 一億零三百一十萬零三百一十
1,000,101,234 1,000,101,234 十億零一十萬一千二百三十四
1,000,000,000,310 1,000,000,000,310 一兆零三百一十
1,000,000,100,000 1,000,000,100,000 一兆零一十萬
附上完整程式碼(含測試),需要的同學請自取,如發現 Bug 歡迎留言反應,感謝。
using System.Text;
using System.Text.RegularExpressions;
var numSets = new[] {
"0000", "0001", "0010", "0012",
"0100", "0102", "0310", "0123",
"1000", "1002", "1020", "1023",
"1200", "1203", "1230", "1234"
};
// 列出所有可能的組合
var count = numSets.Length;
var idxs = new int[3];
var lv = idxs.Length - 1;
while (true)
{
var sb = new StringBuilder();
for (var i = 0; i < idxs.Length; i++)
{
sb.Append(numSets[idxs[i]]);
}
long n = long.Parse(sb.ToString());
var cht = ChtNumConverter.ToChtNum(n);
var restored = ChtNumConverter.ParseChtNum(cht);
Console.WriteLine($"{n, 16:n0} {restored, 16:n0} " +
$"\x1b[{(n == restored ? "32mPASS" : "31mFAIL")}\x1b[0m {cht} ");
if (n != restored)
throw new ApplicationException($"數字轉換錯誤 {n} vs {restored}");
if (idxs.All(o => o == count - 1)) break;
idxs[lv]++;
while (idxs[lv] == count)
{
idxs[lv] = 0;
lv--;
if (lv < 0) break;
idxs[lv]++;
}
lv = idxs.Length - 1;
}
public class ChtNumConverter
{
public static string ChtNums = "零一二三四五六七八九";
public static Dictionary<string, long> ChtUnits = new Dictionary<string, long>{
{"十", 10},
{"百", 100},
{"千", 1000},
{"萬", 10000},
{"億", 100000000},
{"兆", 1000000000000}
};
// 解析中文數字
public static long ParseChtNum(string chtNumString)
{
var isNegative = false;
if (chtNumString.StartsWith("負"))
{
chtNumString = chtNumString.Substring(1);
isNegative = true;
}
long num = 0;
// 處理千百十範圍的四位數
Func<string, long> Parse4Digits = (s) =>
{
long lastDigit = 0;
long subNum = 0;
foreach (var rawChar in s)
{
var c = rawChar.ToString().Replace("〇", "零");
if (ChtNums.Contains(c))
{
lastDigit = (long)ChtNums.IndexOf(c);
}
else if (ChtUnits.ContainsKey(c))
{
if (c == "十" && lastDigit == 0) lastDigit = 1;
long unit = ChtUnits[c];
subNum += lastDigit * unit;
lastDigit = 0;
}
else
{
throw new ArgumentException($"包含無法解析的中文數字:{c}");
}
}
subNum += lastDigit;
return subNum;
};
// 以兆億萬分割四位值個別解析
foreach (var splitUnit in "兆億萬".ToArray())
{
var pos = chtNumString.IndexOf(splitUnit);
if (pos == -1) continue;
var subNumString = chtNumString.Substring(0, pos);
chtNumString = chtNumString.Substring(pos + 1);
num += Parse4Digits(subNumString) * ChtUnits[splitUnit.ToString()];
}
num += Parse4Digits(chtNumString);
return isNegative ? -num : num;
}
// 轉換為中文數字
public static string ToChtNum(long n)
{
var negtive = n < 0;
if (negtive) n = -n;
if (n >= 10000 * ChtUnits["兆"])
throw new ArgumentException("數字超出可轉換範圍");
var unitChars = "千百十".ToArray();
// 處理 0000 ~ 9999 範圍數字
Func<long, string> Conv4Digits = (subNum) =>
{
var sb = new StringBuilder();
foreach (var c in unitChars)
{
if (subNum >= ChtUnits[c.ToString()])
{
var digit = subNum / ChtUnits[c.ToString()];
subNum = subNum % ChtUnits[c.ToString()];
sb.Append($"{ChtNums[(int)digit]}{c}");
}
else sb.Append("零");
}
sb.Append(ChtNums[(int)subNum]);
return sb.ToString();
};
var numString = new StringBuilder();
var forceRun = false;
foreach (var splitUnit in "兆億萬".ToArray())
{
var unit = ChtUnits[splitUnit.ToString()];
if (n < unit)
{
if (forceRun) numString.Append("零");
continue;
}
forceRun = true;
var subNum = n / unit;
n = n % unit;
if (subNum > 0)
numString.Append(Conv4Digits(subNum).TrimEnd('零') + splitUnit);
else numString.Append("零");
}
numString.Append(Conv4Digits(n));
var t = Regex.Replace(numString.ToString(), "[零]+", "零");
if (t.Length > 1) t = t.Trim('零');
t = Regex.Replace(t, "^一十", "十");
return (negtive ? "負" : string.Empty) + t;
}
}
This article provide example of converting Arabic numerals and Chinese numerals.
Comments
Be the first to post a comment