2016-08-22 180 views
1

這是爲了檢查我的實用方法,如果一個replacement string是有效的:檢查替換字符串是有效

public static boolean isValidReplacementString(String regex, String replacement) { 
    try { 
     "".replaceFirst(regex, replacement); 
     return true; 
    } catch (IllegalArgumentException | NullPointerException e) { 
     return false; 
    } 
} 

我想執行的真正替代,因爲獲取源字符串(S)前檢查,這是昂貴(I/O)。

我覺得這個解決方案很拗口。標準庫中是否存在缺少的方法?


編輯: As pointed out by sln,這不,即使沒有找到匹配的工作。


編輯: Following shmosel's answer,我想出了這個 「解決方案」:

private static boolean isLower(char c) { 
    return c >= 'a' && c <= 'z'; 
} 

private static boolean isUpper(char c) { 
    return c >= 'A' && c <= 'Z'; 
} 

private static boolean isDigit(char c) { 
    return isDigit(c - '0'); 
} 

private static boolean isDigit(int c) { 
    return c >= 0 && c <= 9; 
} 

@SuppressWarnings("unchecked") 
public static void checkRegexAndReplacement(String regex, String replacement) { 
    Pattern parentPattern = Pattern.compile(regex); 
    Map<String, Integer> namedGroups; 
    int capturingGroupCount; 

    try { 
     Field namedGroupsField = Pattern.class.getDeclaredField("namedGroups"); 
     namedGroupsField.setAccessible(true); 
     namedGroups = (Map<String, Integer>) namedGroupsField.get(parentPattern); 
     Field capturingGroupCountField = Pattern.class.getDeclaredField("capturingGroupCount"); 
     capturingGroupCountField.setAccessible(true); 
     capturingGroupCount = capturingGroupCountField.getInt(parentPattern); 
    } catch (NoSuchFieldException | IllegalAccessException e) { 
     throw new RuntimeException("That's what you get for using reflection!", e); 
    } 

    int groupCount = capturingGroupCount - 1; 

    // Process substitution string to replace group references with groups 
    int cursor = 0; 

    while (cursor < replacement.length()) { 
     char nextChar = replacement.charAt(cursor); 
     if (nextChar == '\\') { 
      cursor++; 
      if (cursor == replacement.length()) 
       throw new IllegalArgumentException(
         "character to be escaped is missing"); 
      nextChar = replacement.charAt(cursor); 
      cursor++; 
     } else if (nextChar == '$') { 
      // Skip past $ 
      cursor++; 
      // Throw IAE if this "$" is the last character in replacement 
      if (cursor == replacement.length()) 
       throw new IllegalArgumentException(
         "Illegal group reference: group index is missing"); 
      nextChar = replacement.charAt(cursor); 
      int refNum = -1; 
      if (nextChar == '{') { 
       cursor++; 
       StringBuilder gsb = new StringBuilder(); 
       while (cursor < replacement.length()) { 
        nextChar = replacement.charAt(cursor); 
        if (isLower(nextChar) || 
          isUpper(nextChar) || 
          isDigit(nextChar)) { 
         gsb.append(nextChar); 
         cursor++; 
        } else { 
         break; 
        } 
       } 
       if (gsb.length() == 0) 
        throw new IllegalArgumentException(
          "named capturing group has 0 length name"); 
       if (nextChar != '}') 
        throw new IllegalArgumentException(
          "named capturing group is missing trailing '}'"); 
       String gname = gsb.toString(); 
       if (isDigit(gname.charAt(0))) 
        throw new IllegalArgumentException(
          "capturing group name {" + gname + 
            "} starts with digit character"); 
       if (namedGroups == null || !namedGroups.containsKey(gname)) 
        throw new IllegalArgumentException(
          "No group with name {" + gname + "}"); 
       refNum = namedGroups.get(gname); 
       cursor++; 
      } else { 
       // The first number is always a group 
       refNum = (int)nextChar - '0'; 
       if (!isDigit(refNum)) 
        throw new IllegalArgumentException(
          "Illegal group reference"); 
       cursor++; 
       // Capture the largest legal group string 
       boolean done = false; 
       while (!done) { 
        if (cursor >= replacement.length()) { 
         break; 
        } 
        int nextDigit = replacement.charAt(cursor) - '0'; 
        if (!isDigit(nextDigit)) { // not a number 
         break; 
        } 
        int newRefNum = (refNum * 10) + nextDigit; 
        if (groupCount < newRefNum) { 
         done = true; 
        } else { 
         refNum = newRefNum; 
         cursor++; 
        } 
       } 
      } 
      if (refNum < 0 || refNum > groupCount) { 
       throw new IndexOutOfBoundsException("No group " + refNum); 
      } 
     } else { 
      cursor++; 
     } 
    } 
} 

如果此方法拋出,無論是正則表達式或替換字符串是無效的。

這比replaceAllreplaceFirst更嚴格,因爲如果找不到匹配,這些方法將不會調用appendReplacement,因此「缺少」無效的組引用。

+1

我不確定引擎是否會檢查替換字符串,如果沒有匹配,我可能是錯的。也就是說,替換時的一些錯誤可能是非正則表達式中沒有定義的捕獲組反向引用。 – sln

+0

替換前使用apache StringUtils.isNotNull方法檢查null。 – amitmah

+0

@sln你說得對。 'isValidReplacementString(「test」,「$」)'因爲沒有找到匹配而返回'true'。所以我的方法甚至不能正常工作。 – xehpuk

回答

1

我想說你最好的選擇是複製Matcher.appendReplacement()中實現的過程,刪除任何有關源字符串或結果字符串的邏輯。這不可避免地意味着你將無法進行某些驗證,例如驗證組名和索引,但是你應該能夠應用其中的大部分驗證。

+0

我已將「appendReplacement」的主體並使其可運行(在問題中編輯)。與你的期望相反,現在更嚴格,如果沒有找到匹配,也會拋出。這可能是最好的。 – xehpuk

+0

@xehpuk你說得對,我把源字符串與正則表達式混淆了。 – shmosel

相關問題