一、一个开始
兴趣盎然之际百度搜索到一篇豆瓣http://www.douban.com/note/151137991/?start=100
拜读之后发现,古人的思维和对数的理解能力是比较夸张的。引来我对阿拉伯数字转中文数字的新兴趣。中国古代的计数也有好几种,一些大的计数单位受到了古印度佛教的影响。
看到那些往大了去的计数单位不知道有多少个0在后面的计数单位,眼都花了。这么多的位数需要使用到的科学计数法也很夸张。
想想是不是能把这些玩意也都转成中文了,位数不限?只要扩展单位就可以了。
以前看过古人计数的一些文字,但是已经不知去向了,遂只能百度百科之。
http://baike.baidu.com/view/149761.htm
看完这里截取一下:
“《五经算术》:
按黄帝为法,数有十等。及其用也,乃有三焉。
十等者,谓“亿、兆、京、垓、秭、穰、沟、涧、正、载”也。
三等者,谓“上、中、下”也。
下数者,十十变之。若言十万曰亿,十亿曰兆,十兆曰京也。
中数者,万万变之。若言万万曰亿,万亿曰兆,万兆曰京也。
上数者,数穷则变。若言万万曰亿,亿亿曰兆、兆兆曰京也。”
下数和中数都没啥搞头,上数就蛮有意思。数字单位结构就像一个金字塔堆叠起来。
如图所示:
二、规律小结
结合以前做的一些小研究,按照反复实验推算出各个单位的一些规律,那些太夸张的单位就忽略不计了……
规律如下表所示:
中文单位 | 数量 | 第一次出现的位置 |
---|---|---|
拾 | 10^1 | 1+1 |
佰 | 10^2 | 2+1 |
仟 | 10^3 | 3+1 |
万 | 10^(2^2) | 2^2+1 |
亿 | 10^(2^3) | 2^3+1 |
兆 | 10^(2^4) | 2^4+1 |
京 | 10^(2^5) | 2^5+1 |
垓 | 10^(2^6) | 2^6+1 |
杼 | 10^(2^7) | 2^7+1 |
穰 | 10^(2^8) | 2^8+1 |
沟 | 10^(2^9) | 2^9+1 |
涧 | 10^(2^10) | 2^10+1 |
正 | 10^(2^11) | 2^11+1 |
载 | 10^(2^12) | 2^12+1 |
极 | 10^(2^13) | 2^13+1 |
恒河沙 | 10^(2^14) | 2^14+1 |
阿僧祇 | 10^(2^15) | 2^15+1 |
那由他 | 10^(2^16) | 2^16+1 |
不可思议 | 10^(2^17) | 2^17+1 |
无量 | 10^(2^18) | 2^18+1 |
大数 | 10^(2^19) | 2^19+1 |
好吧,这些东西有什么用?
这个在后面的计算方法中有使用到。为确定是否在目标字符串中添加该单位。
这里涉及三个概念如下:
用于计算步长和该单位前是否有值的范围,也就是2和3。
2.步长
所谓步长指的是每个单位出现的间隔。
公式:(第一次出现位置-1)*2
比如亿是每隔(2^3)*2=16位出现的。
3.判断在该单位前是否有值的范围上限这里解释一下,中文数字规则里面,单位之前有值的,必须接上该单位,如果单位之前全是0那么该单位忽略不计。
公式:第一次出现位置-2
比如亿的判断范围是(2^3)-1=7。就是该单位前面7位如果有非零,那么在扫描到该单位位置的时候要加上“亿”,否则无视之。
除此之外,还可以从表看出,有3个单位与众不同,或许因为他们太小了……
那么规律就是“拾”、“佰”、“仟”不用计算他们之前是否有值。
这里的各种计算出自愚钝的楼主头脑风暴,不一定合适各位聪明的看客。各位看客有兴趣自己研究一下。
三、代码
根据这种明显的不同我给他们分了成了两部分,一部分叫BigUnit,一部分叫SmallUnit。好吧不同单位也是不同的~逻辑部分和以前写的c#版阿拉伯数字转中文是一致的,只不过那里最高只有单位“亿”。下面列出点成果:
单位枚举:
//10^(2^n) 对应第一次出现位置为 2^n+1
public enum UnitFirstPos
{
拾 = 2,//10^1 1+1
佰 = 3,//10^2 2+1
仟 = 4,//10^3 3+1
万 = 5,//10^(2^2) 2^2+1
亿 = 9,//10^(2^3) 2^3+1
兆 = 17,//10^(2^4) 2^4+1
京 = 33,//10^(2^5) 2^5+1
垓 = 65,//10^(2^6) 2^6+1
杼 = 129,//10^(2^7) 2^7+1
穰 = 257,//10^(2^8) 2^8+1
沟 = 513,//10^(2^9) 2^9+1
涧 = 1025,//10^(2^10) 2^10+1
正 = 2049,//10^(2^11) 2^11+1
载 = 4097,//10^(2^12) 2^12+1
极 = 8193,//10^(2^13) 2^13+1
恒河沙 = 16385,//10^(2^14) 2^14+1
阿僧祇 = 32769,//10^(2^15) 2^15+1
那由他 = 65537,//10^(2^16) 2^16+1
不可思议 = 131073,//10^(2^17) 2^17+1
无量 = 262145,//10^(2^18) 2^18+1
大数 = 524289//10^(2^19) 2^19+1
}
单位抽象类:
public abstract class AUnit
{
/// <summary>
/// 单位名
/// </summary>
protected string m_Name = string.Empty;
/// <summary>
/// 开始位置
/// </summary>
protected int m_FirstPosition = 0;
/// <summary>
/// 步长(每一个单位都有其步长,就是每几位出现一次,每个单位出现都有一个周期)
/// </summary>
protected int m_Stepn = 0;
/// <summary>
/// 判断有值的范围
/// 例如万位有值,则是因为万位前3位有值
/// </summary>
protected int m_HasValuePosition = 0;
/// <summary>
/// 判断有值之后,则设此标志为true
/// 待之后完成单位处理之后重置为false
/// </summary>
protected bool m_HasValue = false;
public AUnit(UnitFirstPos ufp)
{
this.m_Name = ufp.ToString();
this.m_FirstPosition = (int)ufp;
//步长计算方法:(第一次出现位置-1)*2
this.m_Stepn = (this.m_FirstPosition - 1) * 2;
//判断有值的范围:第一次出现位置-2
this.m_HasValuePosition = this.m_FirstPosition - 2;
}
public virtual string GetValue(int currentPosition) { return string.Empty; }
public virtual string GetValue(int currentPosition, bool isNotZero) { return string.Empty; }
}
BigUnit
/// <summary>
/// big unit 从万开始
/// </summary>
public class BigUnit : AUnit
{
public BigUnit(UnitFirstPos ufp)
: base(ufp) { }
/// <summary>
/// big unit 需要根据当前位,以及当前位是否为零,取值
/// </summary>
/// <param name="currentPosition">当前位</param>
/// <param name="isNotZero">当前位是否为'0'</param>
/// <returns></returns>
public override string GetValue(int currentPosition, bool isNotZero)
{
//判断当前位是否有值【(当前位置-第一次出现位置)%步长】在【0~判断有值的范围】内
//并且当前位必须不为'0'
int condition = (currentPosition - this.m_FirstPosition) % this.m_Stepn;
if (condition <= this.m_HasValuePosition
&& condition >= 0
&& isNotZero)
{
//设置单位有值标志
this.m_HasValue = true;
}
//在单位前有值的情况下,当前位置出现在单位的步长位上
if (this.m_HasValue && condition == 0)
{
//重置单位有值标志
this.m_HasValue = false;
//返回单位名称
return this.m_Name;
}
else
{
return string.Empty;
}
}
}
SmallUnit
/// <summary>
/// small unit(十佰仟)
/// 步长固定为4,没有有值范围的判断
/// </summary>
public class SmallUnit : AUnit
{
public SmallUnit(UnitFirstPos ufp)
: base(ufp)
{
base.m_Stepn = 4;
}
/// <summary>
/// small unit(十佰仟) 只需要根据当前位,取值
/// </summary>
/// <param name="currentPosition">当前位</param>
/// <returns></returns>
public override string GetValue(int currentPosition)
{
//当前位置出现在单位的步长位上
if ((currentPosition - this.m_FirstPosition) % this.m_Stepn == 0)
{
return m_Name;
}
else
{
return string.Empty;
}
}
}
逻辑部分:
class ArabicNumToCnNum
{
private static readonly string[] CN_NUM = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };
private static readonly string[] CN_DECIMAL_NUM = { "角", "分", "厘" };
/// <summary>
/// 阿拉伯数字表示的数转换为中文大写的数字
/// 不考虑输入是否合法
/// </summary>
/// <param name="num"></param>
/// <returns></returns>
public static string ToCn(string num)
{
SmallUnit shi = new SmallUnit(UnitFirstPos.拾);
SmallUnit bai = new SmallUnit(UnitFirstPos.佰);
SmallUnit qian = new SmallUnit(UnitFirstPos.仟);
BigUnit wan = new BigUnit(UnitFirstPos.万);
BigUnit yi = new BigUnit(UnitFirstPos.亿);
BigUnit zhao = new BigUnit(UnitFirstPos.兆);
BigUnit jing = new BigUnit(UnitFirstPos.京);
BigUnit gai = new BigUnit(UnitFirstPos.垓);
BigUnit zhu = new BigUnit(UnitFirstPos.杼);
BigUnit rang = new BigUnit(UnitFirstPos.穰);
BigUnit gou = new BigUnit(UnitFirstPos.沟);
BigUnit jian = new BigUnit(UnitFirstPos.涧);
BigUnit zheng = new BigUnit(UnitFirstPos.正);
BigUnit zhai = new BigUnit(UnitFirstPos.载);
BigUnit ji = new BigUnit(UnitFirstPos.极);
BigUnit henghesha = new BigUnit(UnitFirstPos.恒河沙);
BigUnit asengzhi = new BigUnit(UnitFirstPos.阿僧祇);
BigUnit nayouta = new BigUnit(UnitFirstPos.那由他);
BigUnit bukesiyi = new BigUnit(UnitFirstPos.不可思议);
BigUnit wuliang = new BigUnit(UnitFirstPos.无量);
BigUnit dashu = new BigUnit(UnitFirstPos.大数);
StringBuilder des = new StringBuilder(string.Empty);
bool preZero = false;//前一位是否为0标志,起始前一位不为零
int length = num.Length;// 字符串长度
for (int i = 0; i < length; i++)
{
int n = length - i;// 从最高位开始扫描,字符串0位置为最高位位置((length-1)开始到0结束)
char cInt = num[i];// 当前字符
bool isNotZero = (cInt != '0');
// 当前位的数字非零
if (isNotZero)
{
// 是否接上零(前一位为0则加上0)
if (preZero)
{
des.Append(CN_NUM[0]);
preZero = false;
}
des.Append(CN_NUM[cInt - '0']);
des.Append(shi.GetValue(n));
des.Append(bai.GetValue(n));
des.Append(qian.GetValue(n));
}
else
{
// 当前位为零则置preZero为true值
preZero = true;
}
des.Append(wan.GetValue(n, isNotZero));
des.Append(yi.GetValue(n, isNotZero));
des.Append(zhao.GetValue(n, isNotZero));
des.Append(jing.GetValue(n, isNotZero));
des.Append(gai.GetValue(n, isNotZero));
des.Append(zhu.GetValue(n, isNotZero));
des.Append(rang.GetValue(n, isNotZero));
des.Append(gou.GetValue(n, isNotZero));
des.Append(jian.GetValue(n, isNotZero));
des.Append(zheng.GetValue(n, isNotZero));
des.Append(zhai.GetValue(n, isNotZero));
des.Append(ji.GetValue(n, isNotZero));
des.Append(henghesha.GetValue(n, isNotZero));
des.Append(asengzhi.GetValue(n, isNotZero));
des.Append(nayouta.GetValue(n, isNotZero));
des.Append(bukesiyi.GetValue(n, isNotZero));
des.Append(wuliang.GetValue(n, isNotZero));
des.Append(dashu.GetValue(n, isNotZero));
}
// 返回目标数组
return des.ToString();
}
}
好了,第一次出现位置在524289位的单位已经很夸张了,想再添加就加上去好了。
各位有兴趣自己测试一下吧,测死不偿命。
好了今天就到这里为止,写的不好,欢迎各位拍砖。