我来写个人民币金额转大写的例子

最近找段转换人民币金额大写的代码,结果放 google 一搜基本上都是些文盲写的垃圾代码,大概看了下他们都是先把数字转成字符串再各种操作的,恶心的不行,就跟看见用循环计算等差数列的和一样。所以,最后趁着放假花了点时间实现了一个比较科学的版本。

为什么我写的这个版本特别科学呢?因为这个版本除了最终输出用了 StringBuilder 以外,在运行的时候不需要任何的动态内存操作,包括字符串操作和容器,只有栈操作和纯粹的运算,性能杠杠的。

/** 中文互联网上迄今为止实现最正确、代码最漂亮且效率最高的大写人民币金额转换代码 
 * 作者:李维 <oldrev@gmail.com>
 * 版权所有 (c) 2013 昆明维智众源企业管理咨询有限公司。保留所有权利。
 * 本代码基于 BSD License 授权。
 * */

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace Sandwych.RmbConverter {

    public static class RmbUpperConverter {

        private static readonly Char[] RmbDigits = {
            '', '', '', '', '', '', '', '', '', '' };

        private static readonly string[] SectionChars = {
             string.Empty, "", "", "", "" };

        public static string ToRmbUpper(this decimal price) {
            if (price < 0M || price >= 9999999999999999.99M) {
                throw new ArgumentOutOfRangeException("price");
            }

            price = Math.Round(price, 2);
            var sb = new StringBuilder();

            var integerPart = (long)price;
            var wanyiPart = integerPart / 1000000000000L;
            var yiPart = integerPart % 1000000000000L / 100000000L;
            var wanPart = integerPart % 100000000L / 10000L;
            var qianPart = integerPart % 10000L;
            var decPart = (long)(price * 100) % 100;

            int zeroCount = 0;
            //处理万亿以上的部分
            if (integerPart >= 1000000000000L && wanyiPart > 0) {
                zeroCount = ParseInteger(sb, wanyiPart, true, zeroCount);
                sb.Append("");
            }

            //处理亿到千亿的部分
            if (integerPart >= 100000000L && yiPart > 0) {
                var isFirstSection = integerPart >= 100000000L && integerPart < 1000000000000L;
                zeroCount = ParseInteger(sb, yiPart, isFirstSection, zeroCount);
                sb.Append("亿");
            }

            //处理万的部分
            if (integerPart >= 10000L && wanPart > 0) {
                var isFirstSection = integerPart >= 1000L && integerPart < 10000000L;
                zeroCount = ParseInteger(sb, wanPart, isFirstSection, zeroCount);
                sb.Append("");
            }

            //处理千及以后的部分
            if (qianPart > 0) {
                var isFirstSection = integerPart < 1000L;
                zeroCount = ParseInteger(sb, qianPart, isFirstSection, zeroCount);
            }
            else {
                zeroCount += 1;
            }

            if (integerPart > 0) {
                sb.Append("");
            }

            //处理小数
            if (decPart > 0) {
                ParseDecimal(sb, integerPart, decPart, zeroCount);
            }
            else if (decPart <= 0 && integerPart > 0) {
                sb.Append("");
            }
            else {
                sb.Append("零元整");
            }

            return sb.ToString();
        }

        private static void ParseDecimal(StringBuilder sb, long integerPart, long decPart, int zeroCount) {
            Debug.Assert(decPart > 0 && decPart <= 99);
            var jiao = decPart / 10;
            var fen = decPart % 10;

            if (zeroCount > 0 && (jiao > 0 || fen > 0) && integerPart > 0) {
                sb.Append("");
            }

            if (jiao > 0) {
                sb.Append(RmbDigits[jiao]);
                sb.Append("");
            }
            if (zeroCount == 0 && jiao == 0 && fen > 0 && integerPart > 0) {
                sb.Append("");
            }
            if (fen > 0) {
                sb.Append(RmbDigits[fen]);
                sb.Append("");
            }
            else {
                sb.Append("");
            }
        }

        private static int ParseInteger(StringBuilder sb, long integer, bool isFirstSection, int zeroCount) {
            Debug.Assert(integer > 0 && integer <= 9999);
            int nDigits = (int)Math.Floor(Math.Log10(integer)) + 1;
            if (!isFirstSection && integer < 1000) {
                zeroCount++;
            }
            for (var i = 0; i < nDigits; i++) {
                var factor = (long)Math.Pow(10, nDigits - 1 - i);
                var digit = integer / factor;

                if (digit != 0) {
                    if (zeroCount > 0) {
                        sb.Append("");
                    }
                    sb.Append(RmbDigits[digit]);
                    sb.Append(SectionChars[nDigits - i - 1]);
                    zeroCount = 0;
                }
                else {
                    zeroCount++;
                }
                integer -= integer / factor * factor;
            }
            return zeroCount;
        }

    }

}

 

单元测试,及 Python(嗯,我就是这么好心,又用 Python 重新实现了一遍) 实现版本什么的都放在 GitHub 上了:https://github.com/Sandwych/rmb_converter

以后写程序动动脑子,你们可长点心吧!

posted on 2013-09-20 16:49  李屠户  阅读(1096)  评论(0编辑  收藏  举报