程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Merlin的魔力: 格式化數值和貨幣

Merlin的魔力: 格式化數值和貨幣

編輯:關於JAVA

java.text 包允許通過與特定語言無關的方式格式化文本消息、日期和數值。許多人配合 MessageFormat 類使用資源包來為用戶本地化消息。更多的人似乎使用 DateFormat 和 SimpleDateFormat 類來操作日期字符串,既用於輸入也用於輸出。最少見的用法似乎是使用 NumberFormat 類及其相關的子類 DecimalFormat 和 ChoiceFormat 。在本月的討論中,我們將研究一下這三個未得到充分利用的類以及 Currency 類,看看 J2SE 1.4 已經變得有多麼的全球化。

數值格式化基類:NumberFormat

如果您來自美國,您會在較大的數值中間放置逗號來表示千和百萬(等等,每三個數值使用一個逗號)。對於浮點數,您將在整數部分和小數部分之間放置小數點。對於金錢,貨幣符號 $ 放在金額的前面。如果 您從來沒有到過美國以外的地方,可能就不會關心用元(¥)來格式化的日本貨幣,用英鎊(£)來格式化的英國貨幣,或者用歐元(€)來表示的其他歐洲國家的貨幣。

對於那些我們確實關心的貨幣,我們可以使用 NumberFormat 及其相關的類來格式化它們。開發人員使用 NumberFormat 類來讀取用戶輸入的數值,並格式化將要顯示給用戶看的輸出。

與 DateFormat 類似, NumberFormat 是一個抽象類。您永遠不會創建它的實例――相反,您總是使用它的子類。雖然可以通過子類的構造函數直接創建子類,不過 NumberFormat 類提供了一系列 get XXXInstance() 方法,用以獲得不同類型的數值類的特定地區版本。這樣的方法共有五個:

getCurrencyInstance()

getInstance()

getIntegerInstance()

getNumberInstance()

getPercentInstance()

具體使用哪一個方法取決於您想要顯示的數值類型(或者想要接受的輸入類型)。每個方法都提供了兩個版本――一個版本適用於當前地區,另一個版本接受一個 Locale作為參數,以便可能地指定一個不同的地區。

在 J2SE 1.4中, NumberFormat 新增的內容是 getIntegerInstance()、 getCurrency() 和 setCurrency() 方法。下面讓我們研究一下新的 getIntegerInstance() 方法。稍後將會探討 get/set 貨幣方法。

使用 NumberFormat 的基本過程是獲得一個實例並使用該實例。挑選恰當的實例的確需要費一番思量 。通常您不希望使用通用的 getInstance 或者 getNumberInstance() 版本 ,因為您不確切知道您將會得到什麼。相反 ,您會使用像 getIntegerInstance() 這樣的方法 ,因為您希望把某些內容顯示為整數而不需要任何小數值 。清單1展示了這一點 ,我們在其中把數值 54321 顯示為適合於美國和德國的格式。

清單 1. 使用 NumberFormat

import java.text.*;
import java.util.*;
public class IntegerSample {
  public static void main(String args[]) {
   int amount = 54321;
   NumberFormat usFormat =
    NumberFormat.getIntegerInstance(Locale.US);
   System.out.println(usFormat.format(amount));
   NumberFormat germanFormat =
    NumberFormat.getIntegerInstance(Locale.GERMANY);
   System.out.println(germanFormat.format(amount));
  }
}

運行該代碼將產生如清單2所示的輸出。注意第一種格式(美國)中的逗號分隔符和第二種格式中的點號分隔符。

清單 2. NumberFormat 輸出

54,321
54.321

學習如何迭代 DecimalFormat 中的字符

雖然 NumberFormat 是一個抽象類,並且您將通過像 getIntegerInstance() 這樣的各種方法來使用它的實例,但是 DecimalFormat 類提供了該類的一個具體版本。 您可以顯式地指定字符模式,用以確定如何顯示正數、負數、小數和指數。如果不喜歡用於不同地區的預定義格式,您可以創建自己的格式。(在內部,或許 NumberFormat 使用的就是 DecimalFormat 。)基本的 DecimalFormat 功能在 J2SE 平台的 1.4 版中並沒有改變。改變之處在於添加了 formatToCharacterIterator()、 getCurrency() 和 setCurrency() 方法。

我們將快速浏覽一下新的 formatToCharacterIterator 方法及其關聯的 NumberFormat.Field 類。J2SE 1.4 引入了 CharacterIterator 的概念,它允許雙向地遍歷文本。對於 formatToCharacterIterator ,您將獲得它的子接口 AttributedCharacterIterator ,這個子接口允許您找出關於該文本的信息。對於 DecimalFormat 的情況 ,那些屬性是來自 NumberFormat.Field 的鍵 。通過使用 AttributedCharacterIterator , 您完全可以根據所產生的結果構造自己的字符串輸出 。清單3使用了一個百分數實例來提供一個簡單的演示:

清單 3. 使用 formatToCharacterIterator()

import java.text.*;
import java.util.*;
public class DecimalSample {
  public static void main(String args[]) {
   double amount = 50.25;
   NumberFormat usFormat = NumberFormat.getPercentInstance(Locale.US);
   if (usFormat instanceof DecimalFormat) {
    DecimalFormat decFormat = (DecimalFormat)usFormat;
    AttributedCharacterIterator iter =
     decFormat.formatToCharacterIterator(new Double(amount));
     for (char c = iter.first();
       c != CharacterIterator.DONE;
       c = iter.next()) {
      // Get map for current character
      Map map = iter.getAttributes();
      // Display its attributes
      System.out.println("Char: " + c + " / " + map);
   }
   }
  }
}

清單4顯示了程序的輸出(顯示在一小段消息之後,以使其更易於閱讀)。基本上, formatToCharacterIterator() 方法的工作方式與調用 format() 相同,只不過前者除了格式化輸出字符串外,還要使用屬性來標記輸出中的每個字符(例如,位於位置 X 處的字符是否為一個整數?)。將 50.25 顯示為百分數,在美國地區的輸出為“5,025%”。通過檢查輸出 ,除“%”外的每個字符都是整數,包括冒號 。除了數值之外 ,逗號也被標記為一個分組分隔符,百分號被標記為一個百分數。每個數字的屬性都是一個 java.util.Map ,其中每個屬性被顯示為 key=value (鍵=值)的形式。在存在多個屬性的情況下,屬性列表中的屬性之間用逗號分隔。

清單 4. formatToCharacterIterator() 輸出

Char: 5 / {java.text.NumberFormat$Field(integer)=
  java.text.NumberFormat$Field(integer)}
Char: , / {java.text.NumberFormat$Field(grouping separator)=
  java.text.NumberFormat$Field(grouping separator),
  java.text.NumberFormat$Field(integer)=
  java.text.NumberFormat$Field(integer)}
Char: 0 / {java.text.NumberFormat$Field(integer)=
  java.text.NumberFormat$Field(integer)}
Char: 2 / {java.text.NumberFormat$Field(integer)=
  java.text.NumberFormat$Field(integer)}
Char: 5 / {java.text.NumberFormat$Field(integer)=
  java.text.NumberFormat$Field(integer)}
Char: % / {java.text.NumberFormat$Field(percent)=
  java.text.NumberFormat$Field(percent)}

基於值范圍和 ChoiceFormat 確定消息

ChoiceFormat 是 NumberFormat 的另一個具體子類。它的定義和行為在 J2SE 1.4 中沒有改變。 ChoiceFormat 並不會真正幫助您格式化數值,但它的確允許您自定義與某個值關聯的文本。在最簡單的情況下,我們可以設想一下顯示出錯消息的情況。如果存在導致失敗的單個原因,您希望使用單詞“is”。如果有兩個或者多個原因,您希望使用單詞“are”。如清單5所示, ChoiceFormat 允許您把一系列的值映射為不同的文本字符串。

ChoiceFormat 類通常與 MessageFormat 類一起使用,以產生與語言無關的拼接起來的消息。這裡沒有說明的是如何使用 ResourceBundle (它通常與 ChoiceFormat 一起使用)來獲得那些字符串。關於如何使用資源包的信息 ,請參見 參考資料;特別地,“Java 國際化基礎”教程提供了關於這方面的深入討論 。

清單 5. 使用 ChoiceFormat

import java.text.*;
import java.util.*;
public class ChoiceSample {
  public static void main(String args[]) {
   double limits[] = {0, 1, 2};
   String messages[] = {
    "is no content",
    "is one item",
    "are many items"};
   ChoiceFormat formats = new ChoiceFormat(limits, messages);
   MessageFormat message = new MessageFormat("There {0}.");
   message.setFormats(new Format[]{formats});
   for (int i=0; i<5; i++) {
    Object formatArgs[] = {new Integer(i)};
    System.out.println(i + ": " + message.format(formatArgs));
   }
  }
}

執行該程序將產生如清單6所示的輸出:

清單 6. ChoiceFormat 輸出

0: There is no content.
1: There is one item.
2: There are many items.
3: There are many items.
4: There are many items.

使用 Currency 進行貨幣計算

前面提到過的 getCurrency() 和 setCurrency() 方法返回新的 java.util.Currency 類的一個實例。這個類允許訪問不同國家的 ISO 4217 貨幣代碼。雖然自從 getCurrencyInstance() 引入以來您就能配合 NumberFormat 一起使用它,然而除了它們的數字顯示外,您永遠不能獲得或顯示某個地區的貨幣符號。有了 Currency 類,現在很容易就可以做到這一點。

正如前面提到過的 ,貨幣代碼來自ISO 4217。通過傳入某個國家的 Locale 或者貨幣的實際字母代碼, Currency.getInstance() 將返回一個有效的 Currency 對象。 NumberFormat 的 getCurrency() 方法將在創建特定地區的貨幣實例之後做同樣的事情。 清單7顯示了如何獲得貨幣實例,以及如何格式化將要顯示為貨幣的數值。記住這些轉換僅用於顯示。如果需要在貨幣之間轉換金額,應該在確定如何顯示值之前進行轉換。

清單 7. 使用 getCurrencyInstance() 和 Currency

import java.text.*;
import java.util.*;
import java.awt.*;
import javax.swing.*;
public class CurrencySample {
  public static void main(String args[]) {
     StringBuffer buffer = new StringBuffer(100);
    Currency dollars = Currency.getInstance("USD");
    Currency pounds = Currency.getInstance(Locale.UK);
    buffer.append("Dollars: ");
    buffer.append(dollars.getSymbol());
    buffer.append("\n");
    buffer.append("Pound Sterling: ");
    buffer.append(pounds.getSymbol());
    buffer.append("\n-----\n");
    double amount = 5000.25;
    NumberFormat usFormat = NumberFormat.getCurrencyInstance(Locale.US);
    buffer.append("Symbol: ");
    buffer.append(usFormat.getCurrency().getSymbol());
    buffer.append("\n");
    buffer.append(usFormat.format(amount));
    buffer.append("\n");
    NumberFormat germanFormat = 
      NumberFormat.getCurrencyInstance(Locale.GERMANY);
    buffer.append("Symbol: ");
    buffer.append(germanFormat.getCurrency().getSymbol());
    buffer.append("\n");
    buffer.append(germanFormat.format(amount));
    JFrame frame = new JFrame("Currency");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JTextArea ta = new JTextArea(buffer.toString());
    JScrollPane pane = new JScrollPane(ta);
    frame.getContentPane().add(pane, BorderLayout.CENTER);
    frame.setSize(200, 200);
    frame.show();
  }
}

遺憾的是,為歐元或者英鎊返回的貨幣符號不是實際的符號,而是三位的貨幣代碼(來自 ISO 4217)。然而在使用 getCurrencyInstance() 的情況下,實際的符號將會顯示出來,如圖1所示。

圖 1. 看見實際的貨幣符號

結束語

對於軟件全球化來說,所需做的不僅僅是自定義文本消息。雖然把文本消息轉移到資源包中至少完成了工作的一半,但是也不要忘了處理與地區密切相關的數值和貨幣顯示。並不是每個人都像在美國一樣使用冒號和點號來進行數字顯示,每個人都必須處理自己的貨幣細節。雖然我們不必依賴像 $$$.99 這樣的老式 COBOL 圖形字符串,但是通過使用特定於地區的 NumberFormat 實例, 您可以使自己的程序更加國際化。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved