2016-08-22 1 views
1

Das ist meine Dienstprogramm Methode zu überprüfen, ob ein replacement string gültig ist:überprüfen Sie, ob Ersatzzeichenfolge gültig ist

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

Ich mag würde dies zu überprüfen, bevor der realen Ersatz Ausführung, da die Quellzeichenfolge bekommen (n) teuer (I/O).

Ich finde diese Lösung ziemlich hacky. Gibt es bereits eine Methode in der Standardbibliothek, die ich vermisse?


Edit: As pointed out by sln, bedeutet dies nicht auch funktionieren, wenn keine Übereinstimmung gefunden wird.


Edit: Following shmosel's answer, kam ich mit dieser "Lösung" up:

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++; 
     } 
    } 
} 

Wenn diese Methode wirft, entweder die Regex oder die Ersatzzeichenfolge ist ungültig.

Dies ist noch strenger als replaceAll oder replaceFirst, da diese Methoden appendReplacement nicht aufrufen werden, wenn keine Übereinstimmung gefunden wird, daher ungültige Gruppenreferenzen "fehlen".

+1

Ich bin nicht sicher, ob die Engine die Ersetzungszeichenfolge prüfen wird, wenn es keine Übereinstimmung gibt, könnte ich falsch liegen. Einige Fehler zum Zeitpunkt des Ersetzens könnten jedoch ungültig sein. Die Rückreferenz der Erfassungsgruppe ist in der Regex nicht definiert. – sln

+0

Verwenden Sie die Methode "apache StringUtils.isNotNull", um vor dem Ersetzen auf Null zu prüfen. – amitmah

+0

@sln Sie haben Recht. 'isValidReplacementString (" test "," $ ")' gibt 'true' zurück, weil keine Übereinstimmung gefunden wurde. Also meine Methode funktioniert nicht einmal richtig. – xehpuk

Antwort

1

Ich würde sagen, Ihre beste Wette ist es, den in Matcher.appendReplacement() implementierten Prozess zu kopieren, indem Sie jegliche Logik ausschließen, die sich auf die Quellzeichenfolge oder die Ergebniszeichenfolge bezieht. Dies bedeutet zwangsläufig, dass Sie bestimmte Validierungen nicht durchführen können, z. B. Gruppennamen und Indizes validieren, aber Sie sollten die meisten davon anwenden können.

+0

Ich habe den Körper von 'appendReplacement' genommen und es runnable gemacht (in der Frage bearbeiten). Entgegen Ihrer Erwartung ist dies jetzt noch strenger und wirft auch, wenn keine Übereinstimmung gefunden wird. Dies ist wahrscheinlich das Beste, was es bekommen kann. – xehpuk

+0

@xehpuk Sie haben Recht, ich habe die Quellzeichenfolge mit der Regex verwechselt. – shmosel

Verwandte Themen