【轉載】http://www-128.ibm.com/developerworks/cn/java/j-mer08133/
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 輸出
學習如何迭代 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
執行個體, 您可以使自己的程式更加國際化。