兴趣所致:阿拉伯数字转中文

本文从兴趣出发,探讨了将阿拉伯数字转换为中文的过程。作者通过观察和实验总结出数字单位的规律,并提供了代码实现。重点在于理解数字单位的间隔和判断规则,以决定何时在中文表示中加入相应单位。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、一个开始

兴趣盎然之际百度搜索到一篇豆瓣http://www.douban.com/note/151137991/?start=100

拜读之后发现,古人的思维和对数的理解能力是比较夸张的。引来我对阿拉伯数字转中文数字的新兴趣。

中国古代的计数也有好几种,一些大的计数单位受到了古印度佛教的影响。
看到那些往大了去的计数单位不知道有多少个0在后面的计数单位,眼都花了。这么多的位数需要使用到的科学计数法也很夸张。
想想是不是能把这些玩意也都转成中文了,位数不限?只要扩展单位就可以了。


以前看过古人计数的一些文字,但是已经不知去向了,遂只能百度百科之。
http://baike.baidu.com/view/149761.htm
看完这里截取一下:
“《五经算术》:
按黄帝为法,数有十等。及其用也,乃有三焉。
十等者,谓“亿、兆、京、垓、秭、穰、沟、涧、正、载”也。
三等者,谓“上、中、下”也。
下数者,十十变之。若言十万曰亿,十亿曰兆,十兆曰京也。
中数者,万万变之。若言万万曰亿,万亿曰兆,万兆曰京也。
上数者,数穷则变。若言万万曰亿,亿亿曰兆、兆兆曰京也。”

下数和中数都没啥搞头,上数就蛮有意思。数字单位结构就像一个金字塔堆叠起来。

如图所示:


二、规律小结

结合以前做的一些小研究,按照反复实验推算出各个单位的一些规律,那些太夸张的单位就忽略不计了……

规律如下表所示:

计数单位
中文单位数量第一次出现的位置
10^11+1
10^22+1
10^33+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

好吧,这些东西有什么用?

这个在后面的计算方法中有使用到。为确定是否在目标字符串中添加该单位。

这里涉及三个概念如下:

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位的单位已经很夸张了,想再添加就加上去好了。
各位有兴趣自己测试一下吧,测死不偿命。
好了今天就到这里为止,写的不好,欢迎各位拍砖。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值