前面刷过一个题,关于罗马字和阿拉伯数字转换,我之前的文章链接:
https://blog.csdn.net/sleepingboy888/article/details/89814335
最近在看《算法的乐趣》王晓华著,其中的“阿拉伯数字和中文数字”。这篇也是自己学习记录下。
阿拉伯数字转中文数字
具体的思路可以参考书中的段落,文章中只说重点的地方。中文数字中0的表述比较复杂,总结起来为一下三点:
如果对这三条规则是否完善存有疑惑,可以在代码写完以后用测试用例来检查。
需要说明的名词:
比较麻烦的可能是“零”的处理,书中归纳总结的挺好的,我个人稍微变通下。根据日常习惯,其实是阿拉伯数字中连续的零只读一个零。调试代码过程中,个人总结:
public static string NumberToChinese(uint num)
{
string result = "";
if (num == 0)
{
return "零";
}
uint _num = num;
//string[] chn_str = new string[] { "零","壹","贰","叁","肆","伍","陆","柒","捌","玖"};
//string[] unit_value = new string[] { "", "拾", "佰", "仟" };
string[] chn_str = new string[] { "零","一", "二", "三", "四", "五", "六", "七", "八", "九" };
string[] section_value = new string[] { "","万","亿","万亿"};
string[] unit_value = new string[] { "", "十", "百", "千" };
uint section = _num % 10000;
for (int i = 0; _num != 0 && i < 4; i++)
{
if (section == 0)
{
//0不需要考虑节权值,不能出现连续的“零”
if (result.Length > 0 && result.Substring(0, 1) != "零")
{
result = "零" + result;
}
_num = _num / 10000;
section = _num % 10000;
continue;
}
result = section_value[i]+result;
uint unit = section % 10;
for (int j = 0; j<4 ; j++)
{
if (unit == 0)
{
//0不需要考虑位权值,不能出现联系的“零”,每节最后的0不需要
if (result.Length > 0 && result.Substring(0, 1) != "零" && result.Substring(0, 1) != section_value[i])
{
result = "零" + result;
}
}
else
{
result = chn_str[unit] + unit_value[j] + result;
}
section = section / 10;
unit = section % 10;
}
_num = _num / 10000;
section = _num % 10000;
}
if (result.Length > 0 && result.Substring(0, 1) == "零")
{
//清理最前面的"零"
result = result.Substring(1);
}
return result;
}
测试用例代码:
public Dictionary m_dic_test = new Dictionary
{
{ 0,"零" },
{ 1,"一" },
{ 2,"二" },
{ 3,"三" },
{ 4,"四" },
{ 5,"五" },
{ 6,"六" },
{ 7,"七" },
{ 8,"八" },
{ 9,"九" },
{ 10,"一十" },
{ 11,"一十一" },
{ 110,"一百一十" },
{ 111,"一百一十一" },
{ 100,"一百" },
{ 102,"一百零二" },
{ 1020,"一千零二十" },
{ 1001,"一千零一" },
{ 1015,"一千零一十五" },
{ 1000,"一千" },
{ 10000,"一万" },
{ 20010,"二万零一十" },
{ 20001,"二万零一" },
{ 100000,"一十万" },
{ 1000000,"一百万" },
{ 10000000,"一千万" },
{ 100000000,"一亿" },
{ 1000000000,"一十亿" },
{ 1000001000,"一十亿零一千" },
//{ 1000001000,"一十亿一千" },
{ 1000000100,"一十亿零一百" },
{ 200010,"二十万零一十" },
{ 2000105,"二百万零一百零五" },
{ 20001007,"二千万一千零七" },
{ 2000100190,"二十亿零一十万零一百九十" },
{ 1040010000,"一十亿四千零一万" },
{ 200012301,"二亿零一万二千三百零一" },
{ 2005010010,"二十亿零五百零一万零一十" },
{ 4009060200,"四十亿零九百零六万零二百" },
{ 4294967295,"四十二亿九千四百九十六万七千二百九十五" }
};
[TestMethod]
public void TestNumberToChinese()
{
foreach (var item in m_dic_test)
{
Assert.AreEqual(Solutions.NumberToChinese(item.Key), item.Value, "error");
}
}
按照我的标准和书中的不太一样,例如:
{ 1000001000,“一十亿一千” }
这个测试用例不能通过,代码解析为:一十亿零一千,我个人感觉这种读法也能接受。这代码不能到万亿,如果需要识别到万亿级,可以把 uint 类型替换掉。
中文数字转阿拉伯数字
这个的思路相对容易一些,主要是分段和分节,需要注意的点:
public static UInt64 ChineseToNumber(string chinese_str)
{
UInt64 result = 0;
Dictionary dic_section_number = new Dictionary {
{ "万",10000 },{ "亿",100000000 },{ "万亿",1000000000000 }
};
Dictionary dic_unit_number = new Dictionary {
{ "十",10 },{ "百",100 },{ "千",1000 }
};
Dictionary dic_chn_number = new Dictionary {
{ "零",0}, { "一",1}, { "二",2} ,{ "三",3},{ "四",4},{ "五",5},{ "六",6},{ "七",7},{ "八",8},{ "九",9}
};
if (chinese_str.Length == 0)
{
return 0;
}
UInt64 section_value = 0;
for (int i = 0; i < chinese_str.Length; i++)
{
string cur_str = chinese_str.Substring(i, 1);
if (dic_section_number.ContainsKey(cur_str))
{
//万亿的特殊处理
if (cur_str == "万" && i + 1 < chinese_str.Length && chinese_str.Substring(i + 1, 1) == "亿")
{
result += section_value * dic_section_number["万亿"];
i++;
}
else
{
result += section_value * dic_section_number[cur_str];
}
section_value = 0;
}
else
{
if(dic_chn_number.ContainsKey(cur_str))
{
if (i + 1 < chinese_str.Length && dic_unit_number.ContainsKey(chinese_str.Substring(i + 1, 1)))
{
//和数字一起成对出现的 十,百,千
section_value += dic_chn_number[cur_str] * dic_unit_number[chinese_str.Substring(i + 1, 1)];
i++;
}
else
{
section_value += dic_chn_number[cur_str];
}
}
}
}
result += section_value;
return result;
}
//测试函数
[TestMethod]
public void TestChineseToNumber()
{
foreach (var item in m_dic_test)
{
Assert.AreEqual(Solutions.ChineseToNumber(item.Value), item.Key, "error");
}
}
C# UnitTest 初使用