Java字符串格式化

1. String format

格式

The format specifiers for general, character, and numeric types have the following syntax:
   %[argument_index$][flags][width][.precision]conversion
 
The optional argument_index is a decimal integer indicating the position of the argument in the argument list. The first argument is referenced by "1$", the second by "2$", etc.

The optional flags is a set of characters that modify the output format. The set of valid flags depends on the conversion.

The optional width is a non-negative decimal integer indicating the minimum number of characters to be written to the output.

The optional precision is a non-negative decimal integer usually used to restrict the number of characters. The specific behavior depends on the conversion.

The required conversion is a character indicating how the argument should be formatted. The set of valid conversions for a given argument depends on the argument's data type.

 直接看代码

System.out.println(String.format("|%1$-10s|随便写点什么|%2$10s|再加一个数字|%3$10.2f|", "ha", "bd",66.6666));

 打印结果

|ha        |随便写点什么|        bd|再加一个数字|     66.67|

 说明:

%1$-10s :

%  表示后面是一段格式化表达式

1$ 表示占位符,需要用跟在后面的第一个参数替换,1和argument_index相对应

-    表示左对齐,和flags相对应

10 对应于width,代表最小应该占用的宽度,因为需要打印的字符串可能超过width

s   对应于conversion,此处表示打印字符串

 

对于

%3$10.2f:

2表示打印的float精度到小数点后2位

 

对于格式化表达式之外的字符,直接打印

 

2. 对于普通的打印可以通过如下的函数以添加空格方式对齐

public static String padRight(String s, int n) {
     return String.format("%1$-" + n + "s", s);  
}

public static String padLeft(String s, int n) {
    return String.format("%1$" + n + "s", s);  
}

 How can I pad a String in Java

这种方法对英文表现良好,但是对于中文打印非常不理想

 

3. 自定义

考虑一个打印机,英文字符的打印宽度为中文字符的一半;

这种现象和GBK的存储格式比较类似。

 

但是java内置的是Unicode,

首先需要判断中文和普通的ASCII字符,

看一下Unicode中常用中文的位置:

Block名称 开始码位 结束码位 字符数
CJK统一汉字 4E00 9FBB 20924

参见 Unicode、GB2312、GBK和GB18030中的汉字

 

那么对取出来的char ch,如果下面的条件成立,可以基本认定是普通的ASCII码

(ch & 0xFFFF00) == 0

 

现在需要打印出如下的效果

Java字符串格式化_第1张图片
 

商品名最多占用十个汉字宽度,后面至少有一个ASCII空格;

后面数量占用三个汉字宽度,但最后至少有一个ASCII空格;

最后是金额。

 

这里主要考虑商品名超出九个汉字宽度的情况,首先需要知道从哪里截断

/**
 * @param s 待处理的字符串
 * @param lenOnByte 最长的ASCII字符宽度,一个汉字=2倍ASCII字符宽度
 * @return [0]=-1表示没有超出,否则表示截断的最后一个char的index;
 *         [1]存储了s的ASCII宽度
 */
int[] findBreakIdxBasedOnGBK(String s, int lenOnByte) {
    int res[] = {-1, 0};

    int lenCnt = 0;
    int i = 0;
    for (; i < s.length(); i++) {
        char ch = s.charAt(i);
        //gbk单字节可存储
        if ((ch & 0xFFFF00) == 0) {
            lenCnt += 1;
        } else {
            lenCnt += 2;
        }

        if (lenCnt >= lenOnByte) {
            break;
        }
    }

    //表明所有字符没有循环结束已经越界
    //由于可能是正好最后一个char到达临界点,所以要减1
    if (i < s.length() - 1) {
        res[0] = i;
    }

    res[1] = lenCnt;
    System.out.println("break point: " + res[0] + "  #  gbk len: " + res[1]);
    return res;
}

 然后添加空格

/**
 * 右边添加空格用于对齐
 * @param s 待处理的字符串
 * @param sLenOnByte s的ASCII字符宽度
 * @param limitOnByte 最长的ASCII字符宽度
 * @return 拼接完成的字符串
 */
String padStringRightBasedOnGBK(String s, int sLenOnByte, int limitOnByte) {
    StringBuilder sb = new StringBuilder();
    sb.append(s);

    //padding
    for (int i = 0; i < limitOnByte - sLenOnByte; i++) {
        sb.append(" ");
    }

    return sb.toString();
}

应用

List<String> formatGoodsList(List<GoodsItem> goodsItems) {
    List<String> printData = new ArrayList<>();

    StringBuilder sb1 = new StringBuilder();

    for (GoodsItem item : goodsItems) {
        String tmp = item.getGoodsName();

        int[] idxs = findBreakIdxBasedOnGBK(tmp, 18);

        if (idxs[0] == -1) {
        	//如果没有越界
            sb1.append(padStringRightBasedOnGBK(tmp, idxs[1], 20));
        } else {
        	//如果越界,截取
            sb1.append(padStringRightBasedOnGBK(tmp.substring(0, idxs[0] + 1),idxs[1],20));
        }
        sb1.append(padRight(item.getGoodsNum() + "", 6));
        sb1.append(item.getGoodsPrice());

        printData.add(sb1.toString());

        if (idxs[0] != -1)	//如果越界,剩下的部分
            printData.add(item.getGoodsName().substring(idxs[0] + 1));

        sb1.setLength(0);
    }

    return printData;
}

  

最后attach一个测试文件

你可能感兴趣的:(format,中文对齐)