官术网_书友最值得收藏!

5.2.2 字符串的格式化

字符串變量的4種賦值方式對于簡單的賦值來說完全夠用了,即便是兩個字符串拼接,也只需通過加號把兩個目標串連起來即可。但對于復雜的賦值來說就麻煩了,假設現在需要拼接一個很長的字符串,字符串內部包含各種類型的變量,有整型、雙精度型、布爾型、字符型,中間還夾雜著一些起黏合作用的子串,如此一來只能使勁地填寫加號,把各種變量努力加上去,就像有時打印日志調用System.out.println一樣,加號多到讓你眼花繚亂。

為了不讓加號如此橫行霸道,String類型從Java 5開始額外提供了format方法用來格式化這些準備填入字符串的各種變量。具體地說,是在一個模板字符串中填寫類似“%s”“%d”“%f”這樣的記號先占幾個位置,然后給format方法的輸入參數分別指定對應位置的變量名稱,表示這些變量值依次替換模板中的“%s”“%d”“%f”等記號。以上模板串用到的占位記號也叫作格式轉換符,詳細說明見表5-1。

表5-1 字符串模板的格式轉換符

下面是利用format方法格式化單個變量值與多個變量值的代碼例子(完整代碼見本章源碼的src\com\string\string\StrFormat.java):

        // 往字符串中填入另一個字符串
        String fromString=String.format("格式化子串的字符串:%s", "Hello");
        System.out.println("fromString="+fromString);
        // 往字符串中填入字符
        String fromChar=String.format("格式化字符的字符串:%c", 'A');
        System.out.println("fromChar="+fromChar);
        // 往字符串中填入布爾值
        String fromBoolean=String.format("格式化布爾值的字符串:%b", false);
        System.out.println("fromBoolean="+fromBoolean);
        // 往字符串中填入十進制整數
        String fromInt=String.format("格式化整型數的字符串:%d", 255);
        System.out.println("fromInt="+fromInt);
        // 往字符串中填入十六進制數
        String fromOct=String.format("格式化十六進制數的字符串:%o", 255);
        System.out.println("fromOct="+fromOct);
        // 往字符串中填入八進制數
        String fromHex=String.format("格式化八進制數的字符串:%x", 255);
        System.out.println("fromHex="+fromHex);
        // 往字符串中填入浮點數
        String fromFloat=String.format("格式化浮點數的字符串:%f", 3.14);
        System.out.println("fromFloat="+fromFloat);
        // 格式化字符串的時候,同時填充多個變量
        String manyVariable=String.format("以下字符串包括多個變量值:%s,%c,%b,%d,
                               %o,%x,%f", "Hello", 'A', false, 255, 255, 255, 3.14);
        System.out.println("manyVariable="+manyVariable);

觀察上面的代碼,可見大部分的基本類型都支持格式化,除了雙精度類型外。如果雙精度數的精度剛好在浮點數范圍之內,能夠借助標記%f來格式化,如果雙精度數超過了浮點數的精度,還能使用%f格式化嗎?接下來通過以下測試代碼看看3.1415926這個雙精度數會被%f格式化成什么樣子:

        // 注意,若是通過%f格式化雙精度數,則會強制轉換成浮點數
        String fromDouble=String.format("雙精度數格式化后丟失精度的字符串:%f", 3.1415926);
        System.out.println("fromDouble="+fromDouble);

運行以上的測試代碼,打印的日志結果如下:

fromDouble=雙精度數格式化后丟失精度的字符串:3.141593

可見使用%f格式化雙精度數時,超出范圍的小數部分被強行四舍五入了,因而%f并不適合用于直接格式化雙精度數。若想讓雙精度數在格式化時不損失精度,需要事先指定小數點后面保留的位數,比如%.8f表示格式化時保留8位小數部分,f前面的數字越大代表保留的位數越多,雙精度數的數值精度就越高。利用%.8f改寫之前的雙精度數格式化代碼,改寫后的演示代碼如下:

        // 格式化雙精度數時,需要指定小數點后面保留的位數
        String fromDecimal=String.format("格式化雙精度數的字符串:%.8f", 3.1415926);
        System.out.println("fromDecimal="+fromDecimal);

運行以上的演示代碼,程序運行結果如下:

fromDecimal=格式化雙精度數的字符串:3.14159260

從日志信息可見,此時雙精度數的小數部分得以完整地保存下來。

所謂格式化,不單單是按照標記填寫具體數值,還要求字符串格式整齊劃一。譬如統計世界各國人口,列表中的各國人口數值應當右對齊,這樣誰多誰少方能一目了然。既然要求支持對齊,那么得先明確該列數字的最大位數,之后才能在位數范圍內選擇左對齊還是右對齊。整數部分最大位數的標記方式與小數部分的保留位數類似,唯一的區別是整數位數的標記不加點號,而小數位數的標記要加點號,例如%8d表示待格式化的整數將占據8個字符空間,并且默認右對齊、左補空格。倘若要求左對齊,則格式化標記需添加橫線符號,像%-8d表示待格式化的整數在8位空間內左對齊,并且右補空格。有時候數字代表一串編碼,即使未達到最大位數也得在左邊補0,此時格式化標記要在位數前面補充0,代表空出來的位置填0而不是填空格,如標記%08d表示待格式化的整數要求右對齊、左補0。

下面是各種格式化整數位數的代碼例子:

        // 對整數分配固定長度,該整數默認右對齊、左補空格
        String fromLenth=String.format("格式化固定長度(默認右對齊)的整數字符串:(%8d)", 255);
        System.out.println("fromLenth="+fromLenth);
        // 對整數分配固定長度,且該整數左對齊、右補空格
        String fromLeft=String.format("格式化固定長度且左對齊的整數字符串:(%-8d)", 255);
        System.out.println("fromLeft="+fromLeft);
        // 對整數分配固定長度,該整數默認右對齊、左補0
        String fromZero=String.format("格式化固定長度且左補0的整數字符串:(%08d)", 255);
        System.out.println("fromZero="+fromZero);

運行上述的格式化代碼,得到以下日志打印結果:

fromLenth=格式化固定長度(默認右對齊)的整數字符串:(     255)
fromLeft=格式化固定長度且左對齊的整數字符串:(255     )
fromZero=格式化固定長度且左補0的整數字符串:(00000255)

由此可見,格式化后的數字既實現了右對齊,又實現了左對齊,還支持在空位補0。

一旦格式化用得多了,便會出現某個變量需要多次填入的情況,比如“重要的事情說3遍”,后面的句子就得輸入3次,像以下代碼一樣,“別遲到”3個字反復寫了3次:

    // 字符串格式化的時候,可能出現某個變量被多次填入的情況
    String fromRepeat1=String.format("重要的事情說3遍:%s,%s,%s", "別遲到", "別遲到", "別遲到");
    System.out.println("fromRepeat1="+fromRepeat1);

這種做法無疑非常拖沓,不但寫起來費勁,看起來也費神。為此格式化又設計了形如“%n$s”的標記,其中n表示當前標記取的是第幾個參數值,尾巴的s就是普通的格式化標記,中間的美元符號$把兩者隔開。例如,標記%1$s表示當前要取第一個參數,且該參數類型為字符串,于是前述的“重要的事情說3遍”即可簡化為以下代碼:

    // 重復填入某個變量值,可利用“%數字$”的形式,其中“數字$”表示這里取后面的第幾個變量值
    String fromRepeat2=String.format("重要的事情說三遍:%1$s,%1$s,%1$s", "別遲到");
    System.out.println("fromRepeat2="+fromRepeat2);

現在有一個比較常見的業務要求,金額數字通常都要保留小數點后面兩位,像余額寶的每日收益就精確到小數點后兩位的單位“分”。此時采取標記%.2f即可實現要求,但是余額寶內部對賬可不能僅僅保留兩位小數,一般至少保留小數點后3位的單位“厘”,那么對賬用的格式化標記就變成了%.3f。這樣有的場合要求更高精度,有的場合對精度的要求不高,意味著標記%.nf中間的n值是隨時變化著的。若要處理變化的輸入數值,則必須通過方法實現相關功能,也就是需要設計一個新方法,該方法的輸入參數包括待格式化的數字和需要保留的小數位數,方法的返回值為截取指定小數位的字符串。

對于雙精度數字來說,此時要先根據小數位數構建一個形如%.nf的格式化標記串,再依據該標記格式化最終的數值字符串。由于百分號“%”是格式化的保留字符,因此要用兩個百分號“%%”來表達一個百分符號“%”,于是雙精度數的小數位數格式化代碼如下:

    // 對雙精度類型的變量截取小數位,多余部分的數字默認四舍五入
    public static String formatWithDouble(double value, int digit) {
        // 先根據小數位數構建格式化標記串。兩個百分號“%%”可轉義為一個百分符號“%”
        String format=String.format("%%.%df", digit);
        // 再依據該標記將具體數字格式化為字符串
        String result=String.format(format, value);
        return result;
    }

對于大小數類型而言,BigDecimal提供了專門的setScale方法,該方法不但允許指定截取的小數位,還支持設置特定的舍入規則,當然通常情況下使用RoundingMode.HALF_UP代表四舍五入即可。下面便是截取大小數的例子:

    // 對大小數類型的變量截取小數位,可指定多余部分數字的舍入規則
    public static String formatWithBigDecimal(BigDecimal value, int digit) {
        // 大小數類型的setScale方法需要指定明確的舍入規則,其中HALF_UP表示四舍五入
        BigDecimal result=value.setScale(digit, RoundingMode.HALF_UP);
        return result.toString();
    }

接下來,外部分別調用上面的雙精度數格式化方法formatWithDouble和大小數格式化方法formatWithBigDecimal,具體的測試調用代碼如下:

        double normalDecimal=19.895;
        // 保留雙精度數的小數點后面兩位
        String normalResult=formatWithDouble(normalDecimal, 2);
        System.out.println("normalResult="+normalResult);
        BigDecimal bigDecimal=new BigDecimal("123456789012345678.901");
        // 保留大小數的小數點后面兩位
        String bigResult=formatWithBigDecimal(bigDecimal, 2);
        System.out.println("bigResult="+bigResult);

運行上述的精度格式化代碼,輸出以下的日志打印信息:

normalResult=19.90
bigResult=123456789012345678.90

可見無論是雙精度格式化,還是大小數格式化,都實現了四舍五入保留兩位小數的目標。

主站蜘蛛池模板: 同仁县| 扬中市| 朝阳县| 正镶白旗| 社旗县| 海晏县| 鞍山市| 江陵县| 甘肃省| 和龙市| 西峡县| 永福县| 子洲县| 昭通市| 疏勒县| 皋兰县| 美姑县| 贡觉县| 佛冈县| 广水市| 孟津县| 济南市| 句容市| 榆林市| 中阳县| 水富县| 大丰市| 通渭县| 鄂州市| 灵丘县| 宁南县| 阜南县| 班玛县| 永泰县| 紫阳县| 海伦市| 油尖旺区| 错那县| 新余市| 鄂温| 丁青县|