java實戰1——浮點數轉人民幣讀法

YunShell發表於2014-07-27

      為了學習安卓,現在開始從C++角度來學習Java,現在看到了《瘋狂java講義》的第四章,裡面有個字串轉人民幣的例子,書上沒做完,於是把其補充完善。其中最難的部分就是根據零在不同位置,具有不同行為。按照中文讀法,有些零要讀出,有些零不需要讀出。下面將按照兩部分進行講解,首先舉一個例子來說明思路,其次給出Java的原始碼供參考,並且進行測試。這個問題主要利用字串陣列來解決。我們假定要轉換的浮點數整數部分不超過12位。

(1) 思路

1.1 分割整數和小數部分

       對於一個浮點數,先來看個簡單點,不加零。比如12312341234.12。按照中文人民幣的讀法,四位一讀,所以其應該讀做“一百二十三億一千二百三十四萬一千二百三十四元一角二分”。首先將浮點數分為兩個部分,整數部分(IntPart)和小數(FloatPart)部分。如何分割呢?就是將該浮點數直接強制取整得到整數部分,如: IntPart =(int)12312341234.12=12312341234,然後用原浮點數減去整數部分後再乘以100(因為中文讀法最多為角分,為兩位),再進行強制轉為整數。這樣就得到了小數部分。如 12312341234.12-IntPart = 0.12,FloatPart =(int)(0.12*100) = 12。然後分別將小數部分(這時也是個整形)和整形部分在將其變為字串,然後進行第二步,處理整數部分讀法和小數部分讀法。

1.2   整數部分轉中文字串

     前面已經將浮點數分割為兩個部分了,首先先處理整數部分轉中文字串。將字串逐一變為漢字,首先要建立一個漢字型檔(利用陣列實現)。可以這樣 String[] ChineseArr ={“零”,”一”,”二”…}

      然後通過逐一掃描整數字符串,將其對映到漢字陣列中,也就是利用每個數字本身的算術值,進行索引陣列,做對映。比如 1.其數字為1,那麼就對映到漢字陣列中ChineseArr[1]正好就是漢字“一“。然後將其加入到結果字串中。對於單位的處理,也是利用一個單位陣列來進行對映。String[] UnitArr ={“千”,”百”,”十”}。個位不需要單位。如何確保單位的正確匹配呢?這裡就要按照整數部分 4位一劃分:123 1234 1234。這樣就分為3個部分,然後在逐一處理每個部分,每個部分的處理都可以抽象出來,寫成一個子函式,這個函式專門用來處理4位數字變成漢字。比如123 1234 1234將其分為3個部分,”123“,”1234“,”1234“,然後分別呼叫同一個方法後,再進行掃尾處理。

      比如“1234“如何變為漢字串呢?首先逐一掃描字串,將該字元變為數字,然後對映到漢子陣列和單位陣列中。

下面為該函式的完整的實現:

public String NumStr2ChineseStr_IntPartCore(String NumStr)
	{
		String rst = ""; //儲存結果 初始化為空
		int len = NumStr.length(); //獲取NumStr的長度
		
		//補零操作
		if(len < 4) //位數小於4位就前補位
		{
			String[] AddZeroStr = {"0","00","000"};
			NumStr = AddZeroStr[4-len-1] + NumStr;
		}
		len = NumStr.length(); //更新長度 其實 len==4
		
		//掃描至第一個不為0的字元
		int i=0; 
		while(i < len && NumStr.charAt(i)-'0'==0)
					++i;
					
		if(i==len) //四個數都為零 ,就直接輸出一個零 並返回
		{
			rst += "零";
			return rst;
		}
		//四個數不都為0的情況
		for(;i < len; ++i)
		{
			int num = NumStr.charAt(i) - '0';
			if( i != len-1 && num != 0)//不是末尾且不為0
			{
				rst += ChineseArr[num] + UnitArr[i] ;//直接對映漢字和單位
			}
			else
			{	
				//若當前結果字串中最後一位不是“零”,且數字串最後一位不是數字0 這樣對中間2個零的只新增一個“零”如1003
				if( rst.length()>0 && rst.charAt(rst.length()-1) != '零' && NumStr.charAt(len-1)-'0'!= 0)
				{ //四位中第二,三位至少有一個為0且最後一位數字不為0 如1003 則為一千零三
					if((i==1||i==2) && num == 0)
						rst += "零";
				}
				if( i== len - 1 && num !=0)//是最後一位,且數字不為0 不加單位
				{
					rst += ChineseArr[num];
				}
			}
		}
			return  rst; //返回結果字串 
	}

     現在分析上述程式碼,該函式只處理4位的數字串,不足四位的補為4位進行處理。"1234","123",1023","1002","1000",”123”,”3”經過該函式後依次為“一千二百三十四”,"一百二十三",“一千零二十三”,“一千零二”,“一千“,”一百二十三”,”三”。該函式的主要功能是將4位的數字串變為漢字串,函式從第一位不是0的字元開始掃描並對映,由於數字串最末位不單位,所以將4位的數字串分為兩部分,前3位加單位,最後一位不加單位。而對於漢字的讀法,對於4位數字串中中間兩位可能為0的情況,又要加“零”。比如前面的例子中,“1002”,“1023”都讀法中要加“零”。

        所以將前3位處理的第一部分中,進行0的處理。詳細實現見程式碼註釋。但是通過該函式,只能將數字字串化為可讀的漢字串,而對於多部分的4位連線情況中的零則沒有完成,比如一個數字串的整數部分為“12301230123”,則分解3部分後,“123”,“0123”,“0123”,在逐一經過上述函式後,變為“一百二十三”,“一百二十三“,”一百二十三“。必須在連結過程中,進行”億“,”“萬”,“元”單位的連線和零的連線。上述例子:最後應讀成“一百二十三億零一百二十三萬零一百二十三元”。整數部分完整處理程式碼如下。

/*將12位整數部分數字轉為漢字字串
	 */
	public String NumStr2ChineseStr_IntPart(String NumStr)
	{
		int len = NumStr.length(); //獲取字串總長度
		int partNum = (len%4==0) ? (len/4) : (len/4)+1;//將12位內數字分為幾部分
		String Str_yi="",Str_wan="",Str_yuan=""; //分別定義億,萬,元字串
		String FirstStr,SecondStr,ThirdStr; //12位內至多分為三部分
		int tmp_len; //用於計算臨時有效字串長度 (去除0)
		boolean FourValidBit_yuan = false ; //判斷第三部分是否為4位
		switch(partNum)
		{
			case 1: //只有1部分,就是xxx元
			{
				Str_yuan += NumStr2ChineseStr_IntPartCore(NumStr);
				break;
			}
			case 2: //有2部分,就是 xxx萬xxx元
			{//注意 String.substring(beginindex,endindex)中endindex不包含
				int endindex = len - 4 ;
				FirstStr = NumStr.substring(0,endindex);
				Str_wan += NumStr2ChineseStr_IntPartCore(FirstStr)+"萬";
				SecondStr = NumStr.substring(endindex,len);
				Str_yuan += NumStr2ChineseStr_IntPartCore(SecondStr);
				tmp_len = GetVaildStrLen(SecondStr);
				if(0 <tmp_len && tmp_len < 4 )
						Str_yuan = "零"+ Str_yuan;
				break;
			}
			case 3://有3部分,就是 xxx億xxx萬xxx元
			{
				int endindex2 = len - 4; 
				int endindex1 = endindex2 - 4;
				FirstStr = NumStr.substring(0,endindex1);
				Str_yi +=  NumStr2ChineseStr_IntPartCore(FirstStr)+"億";
				SecondStr = NumStr.substring(endindex1,endindex2);
				Str_wan += NumStr2ChineseStr_IntPartCore(SecondStr)+"萬";
				tmp_len = GetVaildStrLen(SecondStr);
				if(0< tmp_len && tmp_len<4 ) //中間過程進行補零
						Str_wan = "零"+Str_wan;
				ThirdStr = NumStr.substring(endindex2,len);
				Str_yuan += NumStr2ChineseStr_IntPartCore(ThirdStr) ;
				tmp_len = GetVaildStrLen(ThirdStr);
				if(0 <tmp_len && tmp_len < 4 )
						Str_yuan = "零"+ Str_yuan;
				else 
				{
					if( tmp_len == 4)
						FourValidBit_yuan  = true;
				}
				break;
			}
		}
		//對結果進行判斷,如果萬位和元位都為0 ,
		if(Str_wan.equals("零萬")) //java中時不能直接比較 Str_wan =="零"
		{
			if(FourValidBit_yuan )//Str_wan為"零萬"且元部分為4位。如12300001234中間需讀出"零"
					Str_wan = "零";
			else //否則就不新增 
					Str_wan = ""; 
		}
		if( Str_yuan.equals("零"))
			Str_yuan = "";
		return Str_yi+Str_wan+Str_yuan+"元";
	}

     分析:該函式將整數部分分為3段分別處理並且加上單位(億,萬),然後最後通過三個結果串的連結處理,返回結果。關鍵還是0的處理。在函式中,分別處理後,有一步是計算有效長度的(非0位的個數),來確定是否要新增“零”。比如“12301230123”中間的“0123”在處理完後,就需要加“零”。具體實現間上原始碼。

1.3 小數部分轉中文字串

       小數部分的實現和整數部分類似,由於小數部分最多有2位。也存在首位是否為0的,是否需要填“零”的問題。具體見下面完整程式碼實現和註釋。

(2)完整實現

import java.util.* ;

public class Sample
{
	private String[] ChineseArr = new String[]{"零","一","二","三","四","五","六","七","八","九"};
	private String[] UnitArr = new String[]{"千","百","十"};
	
	/*四位數的字串轉漢字讀法 
	 * 如果輸入NumStr 不夠4位就前補零,變為4位
	 */
	public String NumStr2ChineseStr_IntPartCore(String NumStr)
	{
		String rst = ""; //儲存結果 初始化為空
		int len = NumStr.length(); //獲取NumStr的長度
		
		//補零操作
		if(len < 4) //位數小於4位就前補位
		{
			String[] AddZeroStr = {"0","00","000"};
			NumStr = AddZeroStr[4-len-1] + NumStr;
		}
		len = NumStr.length(); //更新長度 其實 len==4
		
		//掃描至第一個不為0的字元
		int i=0; 
		while(i < len && NumStr.charAt(i)-'0'==0)
					++i;
					
		if(i==len) //四個數都為零 ,就直接輸出一個零 並返回
		{
			rst += "零";
			return rst;
		}
		//四個數不都為0的情況
		for(;i < len; ++i)
		{
			int num = NumStr.charAt(i) - '0';
			if( i != len-1 && num != 0)//不是末尾且不為0
			{
				rst += ChineseArr[num] + UnitArr[i] ;//直接對映漢字和單位
			}
			else
			{	
				//若當前結果字串中最後一位不是“零”,且數字串最後一位不是數字0 這樣對中間2個零的只新增一個“零”如1003
				if( rst.length()>0 && rst.charAt(rst.length()-1) != '零' && NumStr.charAt(len-1)-'0'!= 0)
				{ //四位中第二,三位至少有一個為0且最後一位數字不為0 如1003 則為一千零三
					if((i==1||i==2) && num == 0)
						rst += "零";
				}
				if( i== len - 1 && num !=0)//是最後一位,且數字不為0 不加單位
				{
					rst += ChineseArr[num];
				}
			}
		}
			return  rst; //返回結果字串 
	}
	/* 獲取數字字串的有效位(不是0的位數)
	 */
	public int GetVaildStrLen(String str)
	{
		int start = 0;
		int len = str.length();
		while(start < len  && str.charAt(start) =='0')
			++ start ;
		return 4 - start;
	}
	/*將12位整數部分數字轉為漢字字串
	 */
	public String NumStr2ChineseStr_IntPart(String NumStr)
	{
		int len = NumStr.length(); //獲取字串總長度
		int partNum = (len%4==0) ? (len/4) : (len/4)+1;//將12位內數字分為幾部分
		String Str_yi="",Str_wan="",Str_yuan=""; //分別定義億,萬,元字串
		String FirstStr,SecondStr,ThirdStr; //12位內至多分為三部分
		int tmp_len; //用於計算臨時有效字串長度 (去除0)
		boolean FourValidBit_yuan = false ; //判斷第三部分是否為4位
		switch(partNum)
		{
			case 1: //只有1部分,就是xxx元
			{
				Str_yuan += NumStr2ChineseStr_IntPartCore(NumStr);
				break;
			}
			case 2: //有2部分,就是 xxx萬xxx元
			{//注意 String.substring(beginindex,endindex)中endindex不包含
				int endindex = len - 4 ;
				FirstStr = NumStr.substring(0,endindex);
				Str_wan += NumStr2ChineseStr_IntPartCore(FirstStr)+"萬";
				SecondStr = NumStr.substring(endindex,len);
				Str_yuan += NumStr2ChineseStr_IntPartCore(SecondStr);
				tmp_len = GetVaildStrLen(SecondStr);
				if(0 <tmp_len && tmp_len < 4 )
						Str_yuan = "零"+ Str_yuan;
				break;
			}
			case 3://有3部分,就是 xxx億xxx萬xxx元
			{
				int endindex2 = len - 4; 
				int endindex1 = endindex2 - 4;
				FirstStr = NumStr.substring(0,endindex1);
				Str_yi +=  NumStr2ChineseStr_IntPartCore(FirstStr)+"億";
				SecondStr = NumStr.substring(endindex1,endindex2);
				Str_wan += NumStr2ChineseStr_IntPartCore(SecondStr)+"萬";
				tmp_len = GetVaildStrLen(SecondStr);
				if(0< tmp_len && tmp_len<4 ) //中間過程進行補零
						Str_wan = "零"+Str_wan;
				ThirdStr = NumStr.substring(endindex2,len);
				Str_yuan += NumStr2ChineseStr_IntPartCore(ThirdStr) ;
				tmp_len = GetVaildStrLen(ThirdStr);
				if(0 <tmp_len && tmp_len < 4 )
						Str_yuan = "零"+ Str_yuan;
				else 
				{
					if( tmp_len == 4)
						FourValidBit_yuan  = true;
				}
				break;
			}
		}
		//對結果進行判斷,如果萬位和元位都為0 ,
		if(Str_wan.equals("零萬")) //java中時不能直接比較 Str_wan =="零"
		{
			if(FourValidBit_yuan )//Str_wan為"零萬"且元部分為4位。如12300001234中間需讀出"零"
					Str_wan = "零";
			else //否則就不新增 
					Str_wan = ""; 
		}
		if( Str_yuan.equals("零"))
			Str_yuan = "";
		return Str_yi+Str_wan+Str_yuan+"元";
	}
	//處理小數字符串部分 最多兩位
	public String NumStr2ChineseStr_FloatPart(String floatStr)
	{
		String rst_jiao = "",rst_fen = "";
		int len  = floatStr.length();
		
		//補零操作
		if(len < 2) //位數小於2位就前補位
		{
			String[] AddZeroStr = {"0","00"};
			floatStr = AddZeroStr[2-len-1] + floatStr;
		}
		for(int i=0 ; i < 2 ; ++i) //2 表示就是兩位 因為只有角分而已
		{
			int num = floatStr.charAt(i) - '0';
			if( i == 0)
			{	
				rst_jiao += ChineseArr[num] ;
				if( num != 0)
						rst_jiao += "角";
			}
			else
			{	
				if(num !=0)
						rst_fen += ChineseArr[num]+"分";
			}
		}
		if( rst_fen.equals("") && (rst_jiao.equals("零")||rst_jiao.equals("零角")))
			rst_jiao = "";
		return rst_jiao + rst_fen;
	}
	/* 將一個有效為15位內的浮點數 變成 人民幣中文讀法
	 */
	public String NumStr2ChineseStr(double Num)
	{
		long IntPart = (long)Num; 
		long floatPart = Math.round((Num - IntPart)*100);
		return NumStr2ChineseStr_IntPart(""+IntPart)+ NumStr2ChineseStr_FloatPart(""+floatPart);
	}
	
	/* 靜態測試方法 
	 */
	public static void Test(String rst, String except)
	{
		if(rst.equals(except))
			System.out.print("test passed\n");
		else
			System.out.println("test failed\n");
	}
	//入口函式
	public static void main(String[] args)
	{
		Sample SampleOj = new Sample(); //方法要麼為靜態方法 或者直接建立物件呼叫其方法
		//測試第一部分
		System.out.print("測試1部分:\n");
		Test(SampleOj.NumStr2ChineseStr(1023.12),"一千零二十三元一角二分");
		Test(SampleOj.NumStr2ChineseStr(1003.02),"一千零三元零二分");
		Test(SampleOj.NumStr2ChineseStr(123.3),"一百二十三元三角");
		Test(SampleOj.NumStr2ChineseStr(123),"一百二十三元");
		Test(SampleOj.NumStr2ChineseStr(1234),"一千二百三十四元");

		//測試第二部分
		System.out.print("\n測試2部分:\n");
		Test(SampleOj.NumStr2ChineseStr(1231234.12),"一百二十三萬一千二百三十四元一角二分");
		Test(SampleOj.NumStr2ChineseStr(1230234.1),"一百二十三萬零二百三十四元一角");
		Test(SampleOj.NumStr2ChineseStr(1230034.02),"一百二十三萬零三十四元零二分");
		Test(SampleOj.NumStr2ChineseStr(1230004),"一百二十三萬零四元");
		Test(SampleOj.NumStr2ChineseStr(1230000.01),"一百二十三萬元零一分");

		//測試第三部分
		System.out.print("\n測試3部分:\n");
		Test(SampleOj.NumStr2ChineseStr(12312341234.1),"一百二十三億一千二百三十四萬一千二百三十四元一角");
		Test(SampleOj.NumStr2ChineseStr(12301231234.02),"一百二十三億零一百二十三萬一千二百三十四元零二分");
		Test(SampleOj.NumStr2ChineseStr(12300230034.02),"一百二十三億零二十三萬零三十四元零二分");
		Test(SampleOj.NumStr2ChineseStr(12300001234.02),"一百二十三億零一千二百三十四元零二分");
		Test(SampleOj.NumStr2ChineseStr(12300000234.12),"一百二十三億零二百三十四元一角二分");
		Test(SampleOj.NumStr2ChineseStr(12300000000.02),"一百二十三億元零二分");
	}
}


總結:首先將數字字串分為兩個部分,整數和小數部分,分開處理,在整數處理部分,又按4位4位來分,先寫測試用例分析,哪些地方需要加零,先實現基本功能,在從特殊測試用例角度打補丁。

還是沒有寫的太詳細,本來已經寫的很詳細,結果儲存出問題了,又要重寫,算了,就簡單點寫。下面不能再這裡寫原稿了。


相關文章