前言
java對於字符串拼接一般都喜歡用String.format("xx",arg)
,如下
那么這個簡單實用的小功能底層是作何實現呢?
源碼
- 從String.java源碼入手,看到內部調用到的其實是
return new Formatter().format(format, args).toString();
- 跟進Formatter.java
在
java.util.Formatter#format(java.util.Locale, java.lang.String, java.lang.Object...)
第2493行方法如下:public Formatter format(Locale l, String format, Object ... args) { ensureOpen(); // index of last argument referenced int last = -1; // last ordinary index int lasto = -1; FormatString[] fsa = parse(format); for (int i = 0; i < fsa.length; i++) { FormatString fs = fsa[i]; int index = fs.index(); try { switch (index) { case -2: // fixed string, "%n", or "%%" fs.print(null, l); break; case -1: // relative index if (last < 0 || (args != null && last > args.length - 1)) throw new MissingFormatArgumentException(fs.toString()); fs.print((args == null ? null : args[last]), l); break; case 0: // ordinary index lasto++; last = lasto; if (args != null && lasto > args.length - 1) throw new MissingFormatArgumentException(fs.toString()); fs.print((args == null ? null : args[lasto]), l); break; default: // explicit index last = index - 1; if (args != null && last > args.length - 1) throw new MissingFormatArgumentException(fs.toString()); fs.print((args == null ? null : args[last]), l); break; } } catch (IOException x) { lastException = x; } } return this; }
其中第九行的
FormatString[] fsa = parse(format);
將字符串按照制定規則拆分成多個,如上訴例子,將返回- 我的名字:
- %s
- ,年齡:
- %s
- 查看字符串內部拆分邏輯在
java.util.Formatter#parse
跟進代碼如下:
private FormatString[] parse(String s) { ArrayList<FormatString> al = new ArrayList<>(); Matcher m = fsPattern.matcher(s); for (int i = 0, len = s.length(); i < len; ) { if (m.find(i)) { // Anything between the start of the string and the beginning // of the format specifier is either fixed text or contains // an invalid format string. if (m.start() != i) { // Make sure we didn't miss any invalid format specifiers checkText(s, i, m.start()); // Assume previous characters were fixed text al.add(new FixedString(s.substring(i, m.start()))); } al.add(new FormatSpecifier(m)); i = m.end(); } else { // No more valid format specifiers. Check for possible invalid // format specifiers. checkText(s, i, len); // The rest of the string is fixed text al.add(new FixedString(s.substring(i))); break; } } return al.toArray(new FormatString[al.size()]); }
通過第三行,顯然是通過正則表達式拆分的,java.util.Formatter#parse的2537行看到正則表達式為:
"%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";
具體邏輯如下
- 新建一個“指針” i,指向字符串的首個字符,如
我的名字:%s,年齡:%d","張三
,初始狀態i為0,指向字符'我'
- 從第i個位置開始,匹配正則表達式
- 匹配成功
- 把第i至m.start()之間的字符添加進來(這段是沒有被正則匹配成功的),比如
我的名字:
- 把匹配成功的字符串添加進來,並把指針下標i改為匹配成功的最后一個元素下標,即m.end();比如
%s
- 把第i至m.start()之間的字符添加進來(這段是沒有被正則匹配成功的),比如
- 匹配失敗
把下標為i以后的所有字串都添加進來
- 循環跳轉到第2步,直到指針i到字符串末尾
- 完成字符串拆分
- 新建一個“指針” i,指向字符串的首個字符,如
- 字符串拆分已經完成,將進行變量填充,具體代碼如下:
for (int i = 0; i < fsa.length; i++) { FormatString fs = fsa[i]; int index = fs.index(); try { switch (index) { case -2: // fixed string, "%n", or "%%" fs.print(null, l); break; case -1: // relative index if (last < 0 || (args != null && last > args.length - 1)) throw new MissingFormatArgumentException(fs.toString()); fs.print((args == null ? null : args[last]), l); break; case 0: // ordinary index lasto++; last = lasto; if (args != null && lasto > args.length - 1) throw new MissingFormatArgumentException(fs.toString()); fs.print((args == null ? null : args[lasto]), l); break; default: // explicit index last = index - 1; if (args != null && last > args.length - 1) throw new MissingFormatArgumentException(fs.toString()); fs.print((args == null ? null : args[last]), l); break; } } catch (IOException x) { lastException = x; } }
- 關於
FormatString
接口的實現類,根據之前代碼得知, 普通字符串是java.util.Formatter.FixedString
類型,而待替換的變量占位符是java.util.Formatter.FormatSpecifier
類型。 - 接口的
java.util.Formatter.FormatString#index
方法,對應了上訴switch代碼的不同分支,即不同的處理策略。 - 普通字符串index是 -2,字符串不作處理,直接添加進去
- 占位符字符串時
fs.print((args == null ? null : args[last]), l);
即把對應位置的參數填充進來。
- 關於