String.Format()底層實現原理


前言

java對於字符串拼接一般都喜歡用String.format("xx",arg),如下

那么這個簡單實用的小功能底層是作何實現呢?

源碼

  1. 從String.java源碼入手,看到內部調用到的其實是

    return new Formatter().format(format, args).toString();

  2. 跟進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);將字符串按照制定規則拆分成多個,如上訴例子,將返回

    1. 我的名字:
    2. %s
    3. ,年齡:
    4. %s
  3. 查看字符串內部拆分邏輯在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%])";

    具體邏輯如下

    1. 新建一個“指針” i,指向字符串的首個字符,如我的名字:%s,年齡:%d","張三,初始狀態i為0,指向字符'我'
    2. 從第i個位置開始,匹配正則表達式
    3. 匹配成功
      1. 把第i至m.start()之間的字符添加進來(這段是沒有被正則匹配成功的),比如我的名字:
      2. 把匹配成功的字符串添加進來,並把指針下標i改為匹配成功的最后一個元素下標,即m.end();比如%s
    4. 匹配失敗

      把下標為i以后的所有字串都添加進來

    5. 循環跳轉到第2步,直到指針i到字符串末尾
    6. 完成字符串拆分
  4. 字符串拆分已經完成,將進行變量填充,具體代碼如下:
    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;
               }
           }
    1. 關於FormatString接口的實現類,根據之前代碼得知, 普通字符串是java.util.Formatter.FixedString類型,而待替換的變量占位符是java.util.Formatter.FormatSpecifier類型。
    2. 接口的java.util.Formatter.FormatString#index方法,對應了上訴switch代碼的不同分支,即不同的處理策略。
    3. 普通字符串index是 -2,字符串不作處理,直接添加進去
    4. 占位符字符串時fs.print((args == null ? null : args[last]), l);即把對應位置的參數填充進來。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM