2009-06-08 40 views
12

在歐洲,小數點以','分隔,我們使用可選的''分隔數千。我允許與貨幣價值:如何解析貨幣金額(美國或歐盟)在Java中的浮動值

  • 美國式123,456.78符號
  • 歐洲風格123.456,78符號

我用接下來的正則表達式(從使用RegexBuddy庫),以驗證輸入。我允許可選的兩位數分數和可選的數千分隔符。

^[+-]?[0-9]{1,3}(?:[0-9]*(?:[.,][0-9]{0,2})?|(?:,[0-9]{3})*(?:\.[0-9]{0,2})?|(?:\.[0-9]{3})*(?:,[0-9]{0,2})?)$ 

我想解析一個貨幣字符串爲浮動。例如

123,456.78應當存儲爲123456.78
123.456,78應當存儲爲123456.78
123.45應當存儲爲123.45
1.234應當存儲爲1234 12.34應當存儲爲12.34

和等等...

有沒有一種簡單的方法來在Java中做到這一點?

public float currencyToFloat(String currency) { 
    // transform and return as float 
} 

使用BigDecimal的,而不是浮點


感謝大家的偉大的答案。我已經改變我的代碼使用BigDecimal而不是浮動。我會將這個問題的前一部分保留爲float,以防止人們犯同樣的錯誤。


下一個代碼示出了從美國和歐盟貨幣轉換到通過的BigDecimal(String)構造接受字符串的函數。這是說一個沒有千分位的分數和一個分數的字符串。

import java.util.regex.Matcher; 
import java.util.regex.Pattern; 


public class TestUSAndEUCurrency { 

    public static void main(String[] args) throws Exception {  
     test("123,456.78","123456.78"); 
     test("123.456,78","123456.78"); 
     test("123.45","123.45"); 
     test("1.234","1234"); 
     test("12","12"); 
     test("12.1","12.1"); 
     test("1.13","1.13"); 
     test("1.1","1.1"); 
     test("1,2","1.2"); 
     test("1","1");    
    } 

    public static void test(String value, String expected_output) throws Exception { 
     String output = currencyToBigDecimalFormat(value); 
     if(!output.equals(expected_output)) { 
      System.out.println("ERROR expected: " + expected_output + " output " + output); 
     } 
    } 

    public static String currencyToBigDecimalFormat(String currency) throws Exception { 

     if(!doesMatch(currency,"^[+-]?[0-9]{1,3}(?:[0-9]*(?:[.,][0-9]{0,2})?|(?:,[0-9]{3})*(?:\\.[0-9]{0,2})?|(?:\\.[0-9]{3})*(?:,[0-9]{0,2})?)$")) 
       throw new Exception("Currency in wrong format " + currency); 

     // Replace all dots with commas 
     currency = currency.replaceAll("\\.", ","); 

     // If fractions exist, the separator must be a . 
     if(currency.length()>=3) { 
      char[] chars = currency.toCharArray(); 
      if(chars[chars.length-2] == ',') { 
       chars[chars.length-2] = '.'; 
      } else if(chars[chars.length-3] == ',') { 
       chars[chars.length-3] = '.'; 
      } 
      currency = new String(chars); 
     } 

     // Remove all commas   
     return currency.replaceAll(",", "");     
    } 

    public static boolean doesMatch(String s, String pattern) { 
     try { 
      Pattern patt = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE); 
      Matcher matcher = patt.matcher(s); 
      return matcher.matches(); 
     } catch (RuntimeException e) { 
      return false; 
     }   
    } 

} 
+1

那麼1.23轉換爲什麼?你的規則是矛盾的。爲什麼不利用客戶端應用程序中的本地化設施? – spender 2009-06-08 16:47:19

+0

1.23轉換爲1.23 – 2009-06-08 16:48:53

+0

好的,我的壞...但真的,試圖破譯這不知道它來自哪裏的地方有一種難聞的氣味。 – spender 2009-06-08 16:50:08

回答

31

要回答一個稍微不同的問題:使用float類型來表示的貨幣價值。 It will bite you。改用base-10類型(如BigDecimal),或者使用整數類型(如intlong)(表示您的價值量 - 例如美元貨幣)。

你不能存儲一個精確的值--123.45作爲浮點數,並且對該值的數學運算(例如乘以稅率)會產生舍入誤差。從該頁面

例子:

float a = 8250325.12f; 
float b = 4321456.31f; 
float c = a + b; 
System.out.println(NumberFormat.getCurrencyInstance().format(c)); 
// prints $12,571,782.00 (wrong) 

BigDecimal a1 = new BigDecimal("8250325.12"); 
BigDecimal b1 = new BigDecimal("4321456.31"); 
BigDecimal c1 = a1.add(b1); 
System.out.println(NumberFormat.getCurrencyInstance().format(c1)); 
// prints $12,571,781.43 (right) 

你不想有錯誤的淤泥,當涉及到金錢。

關於原始問題,我在一段時間內沒有碰到Java,但我知道我想遠離正則表達式來完成這種工作。我看到這個建議;它可能會幫助你。未測試;告誡開發者。

try { 
    String string = NumberFormat.getCurrencyInstance(Locale.GERMANY) 
              .format(123.45); 
    Number number = NumberFormat.getCurrencyInstance(locale) 
              .parse("$123.45"); 
    // 123.45 
    if (number instanceof Long) { 
     // Long value 
    } else { 
     // too large for long - may want to handle as error 
    } 
} catch (ParseException e) { 
// handle 
} 

尋找一個符合您期望看到的規則的區域設置。如果找不到,請依次使用多個,或者創建您自己的custom NumberFormat

我還想考慮強制用戶以單一規範格式輸入值。 123.45和123.456看起來方式對於我的口味來說太相似了,按照您的規則,結果會相差1000倍。This is how millions are lost

-1

快速一個骯髒的黑客可能是:

String input = input.replaceAll("\.,",""); // remove *any* , or . 
long amount = Long.parseLong(input); 

BigDecimal bd = BigDecimal.valueOf(amount).movePointLeft(2); 

//then you could use: 
bd.floatValue(); 
//but I would seriously recommended that you don't use floats for monetary amounts. 

注意如果輸入的形式是### 00,即用正好2位小數,這隻會工作。例如input == "10,022"將打破這個相當幼稚的代碼。

另一種方法是使用BigDecimal(String)構造函數,但您需要將這些歐式樣式數字轉換爲'。'。作爲小數點分隔符,除了去除兩個千位分隔符外。

1

作爲一個廣義的解決方案,您可以嘗試

char[] chars = currency.toCharArray(); 
chars[currency.lastIndexOf(',')] = '.'; 
currency = new String(chars); 

,而不是

if(currency.length()>=3) { 
    char[] chars = currency.toCharArray(); 
    if(chars[chars.length-2] == ',') { 
     chars[chars.length-2] = '.'; 
    } else if(chars[chars.length-3] == ',') { 
     chars[chars.length-3] = '.'; 
    } 
    currency = new String(chars); 
} 

,使小數部分可以是任意長度的。