這是爲了檢查我的實用方法,如果一個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++;
}
}
}
如果此方法拋出,無論是正則表達式或替換字符串是無效的。
這比replaceAll
或replaceFirst
更嚴格,因爲如果找不到匹配,這些方法將不會調用appendReplacement
,因此「缺少」無效的組引用。
我不確定引擎是否會檢查替換字符串,如果沒有匹配,我可能是錯的。也就是說,替換時的一些錯誤可能是非正則表達式中沒有定義的捕獲組反向引用。 – sln
替換前使用apache StringUtils.isNotNull方法檢查null。 – amitmah
@sln你說得對。 'isValidReplacementString(「test」,「$」)'因爲沒有找到匹配而返回'true'。所以我的方法甚至不能正常工作。 – xehpuk