`
lovinchan
  • 浏览: 31079 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

将数字转为中文金额的大写方式(Java版)

阅读更多
/**
 * 程序目的:
 *   从命令行接收一个数,并将其转化为中文金额的大写方式
 * 例如 123.45 --> 壹佰贰拾叁元肆角伍分
 * @author LovinChan
 *
 *   看到网上有很多这样的例子程序,不过觉得很不满意。有些程序我从命名上就觉得
 * 实在是不符合规范,程序里面的算法没有让人明白得很清楚的注释,读上去觉得有
 * 点难度,可读性不强。而且很多程序还存在不少bug,随便一测就测出来了。
 *   所以本人还是决定重新写一下这个程序,并且尽量做到消除不必要的bug。这个程
 * 序我没有用什么很精妙的算法,不过用了一些Java类库中的类,像是正则表达式之类
 * 的东西。由于本人对算法不怎么在行,在做转换操作的时候用的是很笨的方法。望各位
 * 大虾海涵,呵呵。
 * 
 *   程序的注释我尽量写得详细一点,如果觉得这个程序哪里有问题或者是哪里有改进的
 * 地方欢迎随时跟我交流。
 * 我的msn:egg.chenlw@gmail.com
 *    QQ:372133556(注上为什么加我就可以了)
 * 我的blog:http://hi.baidu.com/egg_chen
 * 欢迎交流
 */
public class Trans2RMB {

	/**
	 * 测试程序的可行性
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("\n--------将数字转换成中文金额的大写形式------------\n");
		Trans2RMB t2r = new Trans2RMB();
		String s = t2r.cleanZero(t2r.splitNum(t2r.roundString(t2r.getNum())));
		// 如果转换过后是一个空串,则不输出屏幕
		if(!"".equals(s)) {
			System.out.println("转换成中文后为:" + s);;
		}
		System.out.println("\n---------------------------------------------");
	}
	
	/**
	 * 从命令行接收一个数,在其中调用 checkNum() 方法对其进行
	 * 验证,并返回相应的值
	 * @return 如果输入合法,返回输入的这个数
	 */
	private String getNum() {
		String s = null;
		System.out.println("请输入一个数字(精确到小数点后两位):");
		// 从命令行输入这个浮点数
		java.util.Scanner scanner = new java.util.Scanner(System.in);
		s = scanner.next();
		// 关闭这个Scanner
		scanner.close();
		// 判断用户输入是否合法
		// 若合法,返回这个值;若非法返回 "0"
		if(this.checkNum(s)) {
			return s;
		} else {
			return "";
		}
	}
	
	/**
	 * 判断用户输入的数据是否合法,用户只能输入大于零的数字,不能输入其它字符
	 * @param s String
	 * @return 如果用户输入数据合法,返回 true,否则返回 false
	 */
	private boolean checkNum(String s) {
		// 如果用户输入的数里有非数字字符,则视为非法数据,返回 false
		try {
			float f = Float.valueOf(s);
			// 如果这个数小于零则视为非法数据,返回 false
			if(f < 0) {
				System.out.println("非法数据,请检查!");
				return false;
			}else {
				return true;
			}
		} catch (NumberFormatException e) {
			System.out.println("非法数据,请检查!");
			return false;
		}	
	}
	
	/**
	 * 把用户输入的数以小数点为界分割开来,并调用 numFormat() 方法
	 * 进行相应的中文金额大写形式的转换
	 * 注:传入的这个数应该是经过 roundString() 方法进行了四舍五入操作的
	 * @param s String
	 * @return 转换好的中文金额大写形式的字符串
	 */
	private String splitNum(String s) {
		// 如果传入的是空串则继续返回空串
		if("".equals(s)) {
			return "";
		}
		// 以小数点为界分割这个字符串
		int index = s.indexOf(".");
		// 截取并转换这个数的整数部分
		String intOnly = s.substring(0, index);
		String part1 = this.numFormat(1, intOnly);
		// 截取并转换这个数的小数部分
		String smallOnly = s.substring(index + 1);
		String part2 = this.numFormat(2, smallOnly);
		// 把转换好了的整数部分和小数部分重新拼凑一个新的字符串
		String newS = part1 + part2;
		return newS;
	}
		
	/**
	 * 对传入的数进行四舍五入操作
	 * @param s String 从命令行输入的那个数
	 * @return 四舍五入后的新值
	 */
	private String roundString(String s) {
		// 如果传入的是空串则继续返回空串
		if("".equals(s)) {
			return "";
		}
		// 将这个数转换成 double 类型,并对其进行四舍五入操作
		double d = Double.parseDouble(s);
		// 此操作作用在小数点后两位上
		d = (d * 100 + 0.5) / 100;
		// 将 d 进行格式化
		s = new java.text.DecimalFormat("##0.000").format(d);
		// 以小数点为界分割这个字符串
		int index = s.indexOf(".");
		// 这个数的整数部分
		String intOnly = s.substring(0, index);
		// 规定数值的最大长度只能到万亿单位,否则返回 "0"
		if(intOnly.length() > 13) {
			System.out.println("输入数据过大!(整数部分最多13位!)");
			return "";
		}
		// 这个数的小数部分
		String smallOnly = s.substring(index + 1);
		// 如果小数部分大于两位,只截取小数点后两位
		if(smallOnly.length() > 2) {
			String roundSmall = smallOnly.substring(0, 2);
			// 把整数部分和新截取的小数部分重新拼凑这个字符串
			s = intOnly + "." + roundSmall;
		}
		return s;
	}
	
	/**
	 * 把传入的数转换为中文金额大写形式
	 * @param flag int 标志位,1 表示转换整数部分,0 表示转换小数部分
	 * @param s String 要转换的字符串
	 * @return 转换好的带单位的中文金额大写形式
	 */
	private String numFormat(int flag, String s) {
		int sLength = s.length();
		// 货币大写形式
		String bigLetter[] = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
		// 货币单位
		String unit[] = {"元", "拾", "佰", "仟", "万", 
				// 拾万位到仟万位
				"拾", "佰", "仟",
				// 亿位到万亿位
				"亿", "拾", "佰", "仟", "万"};
		String small[] = {"分", "角"};
		// 用来存放转换后的新字符串
		String newS = "";
		// 逐位替换为中文大写形式
		for(int i = 0; i < sLength; i ++) {
			if(flag == 1) {
				// 转换整数部分为中文大写形式(带单位)
				newS = newS + bigLetter[s.charAt(i) - 48] + unit[sLength - i - 1];
			} else if(flag == 2) {
				// 转换小数部分(带单位)
				newS = newS + bigLetter[s.charAt(i) - 48] + small[sLength - i - 1];
			}
		}
		return newS;
	}
	
	/**
	 * 把已经转换好的中文金额大写形式加以改进,清理这个字
	 * 符串里面多余的零,让这个字符串变得更加可观
	 * 注:传入的这个数应该是经过 splitNum() 方法进行处理,这个字
	 * 符串应该已经是用中文金额大写形式表示的
	 * @param s String 已经转换好的字符串
	 * @return 改进后的字符串
	 */
	private String cleanZero(String s) {
		// 如果传入的是空串则继续返回空串
		if("".equals(s)) {
			return "";
		}
		// 如果用户开始输入了很多 0 去掉字符串前面多余的'零',使其看上去更符合习惯
		while(s.charAt(0) == '零') {
			// 将字符串中的 "零" 和它对应的单位去掉
			s = s.substring(2);
			// 如果用户当初输入的时候只输入了 0,则只返回一个 "零"
			if(s.length() == 0) {
				return "零";
			}
		}
		// 字符串中存在多个'零'在一起的时候只读出一个'零',并省略多余的单位
		/* 由于本人对算法的研究太菜了,只能用4个正则表达式去转换了,各位大虾别介意哈... */
		String regex1[] = {"零仟", "零佰", "零拾"};
		String regex2[] = {"零亿", "零万", "零元"};
		String regex3[] = {"亿", "万", "元"};
		String regex4[] = {"零角", "零分"};
		// 第一轮转换把 "零仟", 零佰","零拾"等字符串替换成一个"零"
		for(int i = 0; i < 3; i ++) {
			s = s.replaceAll(regex1[i], "零");
		}
		// 第二轮转换考虑 "零亿","零万","零元"等情况
		// "亿","万","元"这些单位有些情况是不能省的,需要保留下来
		for(int i = 0; i < 3; i ++) {
			// 当第一轮转换过后有可能有很多个零叠在一起
			// 要把很多个重复的零变成一个零
			s = s.replaceAll("零零零", "零");
			s = s.replaceAll("零零", "零");
			s = s.replaceAll(regex2[i], regex3[i]);
		}
		// 第三轮转换把"零角","零分"字符串省略
		for(int i = 0; i < 2; i ++) {
			s = s.replaceAll(regex4[i], "");
		}
		// 当"万"到"亿"之间全部是"零"的时候,忽略"亿万"单位,只保留一个"亿"
		s = s.replaceAll("亿万", "亿");
		return s;
	}
}

 

  • Trans2RMB.rar (5.4 KB)
  • 描述: 将数字转为中文金额的大写方式(Java版)
  • 下载次数: 196
12
8
分享到:
评论
17 楼 liweizlw 2008-08-22  
呵呵,牛人啊,注释的不错。
16 楼 spkto 2008-06-16  
谢谢,很不错的备注。
15 楼 williamy 2008-06-16  
大学刚毕业的时候去一个公司面试就这个题目,我花了1.5小时写出了程序,可惜不要我
14 楼 jasongreen 2008-06-15  
我收藏了,不介意我收录到我的开源项目里吧。这样的通用函数应该写TestCase的。可以做一些扩展。
num2CNY //人民币
num2zhs //简体中文
num2zht //大写中文
13 楼 lovinchan 2008-06-14  
呵呵,刚才说的这些我确实不是很清楚,不过也就是照着我的习惯来写这个程序的,没有考虑这么多。。不过我一定会改正的
12 楼 ldzsl 2008-06-14  
本人没有对转换规则不是太了解,只是面试时曾有人问过,随便看看算法为楼主做个测试
请输入一个数字(精确到小数点后两位):
10001.01
转换成中文后为:壹万零壹元壹分
转换结果违背
(四) 阿拉伯金额数字角位是“0”,而分位不是“0”时,中文大写金额“元”后面应写“零”字。如¥16,409.02,应写成人民币壹万陆仟肆佰零玖元零贰分;又如¥325.04,应写成人民币叁佰贰拾元零肆分。


零在元位和万位上时,这个零一般可写可不写。
阿拉伯小写金额数字中有“0”时,中文大写应按照汉语语言规律、金额数字构成和防止涂改的要求进行书写。举例如下:

  (一) 阿拉伯数字中间有“0”时,中文大写金额中间可以只写一个“零”字。如¥1,409.50,应写成人民币壹仟肆佰零玖元伍角。

  (二) 阿拉伯数字中间连续有几个“0”,中文大写金额中间可以只写一个“零”字,如¥6,007.14,应写成人民币陆仟零柒元壹角肆分。

  (三) 阿拉伯金额数字万位或元位是“0”,或者数字中间连续有几个“0”,万位、元位也是“0”,但仟位、角位不是“0”时,中文大写金额中可以只写一个零字,也可以不写“零”字,如¥1,680.32,应写成人民币壹仟陆佰捌拾元零三角贰分,或者写成人民币壹仟陆佰捌拾元三角贰分;又如¥107,000.53,应写成人民币壹拾万柒仟元零伍角叁分,或者写成人民币壹拾万柒仟元伍角叁分。

   (四) 阿拉伯金额数字角位是“0”,而分位不是“0”时,中文大写金额“元”后面应写“零”字。如¥16,409.02,应写成人民币壹万陆仟肆佰零玖元零贰分;又如¥325.04,应写成人民币叁佰贰拾元零肆分。

根据《中华人民共和国票据法》的相关规定:
填写票据和结算凭证,必须做到标准化、规范化,要素齐全、数字正确、字迹清晰、不错漏、不潦草,防止涂改。
1、中文大写金额数字如零、壹、贰、叁、肆、伍、陆、柒、捌、玖、拾、佰、仟、万、亿等,应用正楷或行书填写,不得自造简化字。如果金额数字书写中使用繁体字,也应受理(针对银行方)。
2、中文大写金额数字到“元”为止的,在“元”之后,应写“整”(或“正”)字,在“角”之后可以不写“整”(或“正”)字。大写金额数字有“分”的,“分”后面不写“整”(或“正”)字。
3、中文大写在写一般票据和银行支票是不一样的。一般票据的写法上面的天外非仙已经写得很清楚了,银行支票得写法就得把零全写上,不管0在什么位置,要写成零元零角零分。
11 楼 lovinchan 2008-06-14  
其实在不同的地方可以有不同的用法,在这个程序中如果用户输入了非法字符则指定传入一个字符串 "" ,以后遇到这个字符串说明用户输入的是非法字符,则停止转换,转到相应的逻辑当中去。
10 楼 pantangyan 2008-06-14  
"".equals(s) 这样的比较好象不大专业哦。。 要是s == null(虽然从屏幕输入不会为null) 或者s是n个空格符, 那就over了。 我感觉 if(s != null && s.trim().length() > 0) 是比较流行的写法.
9 楼 kou0510 2008-06-14  
小兄弟,呵呵,来看了
8 楼 icewubin 2008-06-14  
中间计算过程请使用BigDecimal不要用double,初学者常犯错误,凡是货币计算从来不用double,稍有不慎,double在不同的CPU上精度丢失和放大的程度是不一样的。
7 楼 mornstar 2008-06-14  
欣赏博主的注释精神。
6 楼 laoliu.org 2008-06-14  
嗯,我也去改了,呵呵,再升级了告诉我一声哈,呵呵。
5 楼 lovinchan 2008-06-14  
啊哈,多谢,马上改过来。。
4 楼 jiyanliang 2008-06-14  
scanner 为什么不close呢
3 楼 laoliu.org 2008-06-13  
另外转到 IT民工棚http://mingongpeng.com 了,博主别介意哈,不行的哈告诉我一声
2 楼 laoliu.org 2008-06-13  
早有这篇文章就好了,几年前做财务系统,里边的多少零多少好一顿折腾
1 楼 liweng12 2008-06-13  
赞态度.赞服务.
学习了!

相关推荐

Global site tag (gtag.js) - Google Analytics