Регулярное выражение, совпадающее с полностью определенными именами классов
Каков наилучший способ сопоставить полное имя класса Java в тексте?
Примеры: java.lang.Reflect
, java.util.ArrayList
, org.hibernate.Hibernate
,
8 ответов
Полное имя класса Java (скажем, "N") имеет структуру
N.N.N.N
Часть "N" должна быть идентификатором Java. Идентификаторы Java не могут начинаться с цифры, но после начального символа они могут использовать любую комбинацию букв и цифр, подчеркиваний или знаков доллара:
([a-zA-Z_$][a-zA-Z\d_$]*\.)*[a-zA-Z_$][a-zA-Z\d_$]*
------------------------ -----------------------
N N
Они также не могут быть зарезервированным словом (например, import
, true
или же null
). Если вы хотите проверить правдоподобие только, выше достаточно. Если вы также хотите проверить правильность, вы должны также проверить список зарезервированных слов.
Идентификаторы Java могут содержать любую букву Unicode вместо "только латинский". Если вы хотите проверить это, используйте классы символов Unicode:
([\p{Letter}_$][\p{Letter}\p{Number}_$]*\.)*[\p{Letter}_$][\p{Letter}\p{Number}_$]*
или, для краткости
([\p{L}_$][\p{L}\p{N}_$]*\.)*[\p{L}_$][\p{L}\p{N}_$]*
Спецификация языка Java (раздел 3.8) содержит все подробности о допустимых именах идентификаторов.
Также см. Ответ на этот вопрос: имена переменных Java Unicode
Шаблон, представленный работами Рено. Но, насколько я могу судить, это всегда будет возвращаться в конце.
Чтобы оптимизировать его, вы можете по существу поменять местами первую половину с последней. Обратите внимание на совпадение точек, которое также необходимо изменить.
Вот моя версия, которая по сравнению с оригиналом работает примерно в два раза быстрее:
String ID_PATTERN = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*";
Pattern FQCN = Pattern.compile(ID_PATTERN + "(\\." + ID_PATTERN + ")*");
Я не могу писать комментарии, поэтому я решил написать ответ.
Вот полностью рабочий класс с тестами, основанный на превосходном комментарии @alan-moore
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.regex.Pattern;
import org.junit.Test;
public class ValidateJavaIdentifier {
private static final String ID_PATTERN = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*";
private static final Pattern FQCN = Pattern.compile(ID_PATTERN + "(\\." + ID_PATTERN + ")*");
public static boolean validateJavaIdentifier(String identifier) {
return FQCN.matcher(identifier).matches();
}
@Test
public void testJavaIdentifier() throws Exception {
assertTrue(validateJavaIdentifier("C"));
assertTrue(validateJavaIdentifier("Cc"));
assertTrue(validateJavaIdentifier("b.C"));
assertTrue(validateJavaIdentifier("b.Cc"));
assertTrue(validateJavaIdentifier("aAa.b.Cc"));
assertTrue(validateJavaIdentifier("a.b.Cc"));
// after the initial character identifiers may use any combination of
// letters and digits, underscores or dollar signs
assertTrue(validateJavaIdentifier("a.b.C_c"));
assertTrue(validateJavaIdentifier("a.b.C$c"));
assertTrue(validateJavaIdentifier("a.b.C9"));
assertFalse("cannot start with a dot", validateJavaIdentifier(".C"));
assertFalse("cannot have two dots following each other",
validateJavaIdentifier("b..C"));
assertFalse("cannot start with a number ",
validateJavaIdentifier("b.9C"));
}
}
Я пришел (самостоятельно) к аналогичному ответу (как ответ Томалака), что-то вроде MMMN:
([a-z][a-z_0-9]*\.)*[A-Z_]($[A-Z_]|[\w_])*
Куда,
M = ([a-z][a-z_0-9]*\.)*
N = [A-Z_]($[A-Z_]|[\w_])*
Однако это регулярное выражение (в отличие от ответа Томалака) делает больше предположений:
Имя пакета (часть M) будет только в нижнем регистре, первый символ M всегда будет строчной буквой, остальные могут сочетать подчеркивание, строчные буквы и цифры.
Имя класса (часть N) всегда начинается с заглавной буквы или подчеркивания, остальные могут смешивать подчеркивание, буквы и цифры. Внутренние классы всегда начинаются с символа доллара ($) и должны соответствовать правилам имени класса, описанным ранее.
Примечание: шаблон \w является шаблоном XSD для букв и цифр (он не включает символ подчеркивания (_))
Надеюсь, это поможет.
Для строки типа com.mycompany.core.functions.CustomFunction я использую ((?:(?:\w+)?\.[a-z_A-Z]\w+)+)
Следующий класс проверяет правильность указанного имени пакета:
import java.util.HashSet;
public class ValidationUtils {
// All Java reserved words that must not be used in a valid package name.
private static final HashSet reserved;
static {
reserved = new HashSet();
reserved.add("abstract");reserved.add("assert");reserved.add("boolean");
reserved.add("break");reserved.add("byte");reserved.add("case");
reserved.add("catch");reserved.add("char");reserved.add("class");
reserved.add("const");reserved.add("continue");reserved.add("default");
reserved.add("do");reserved.add("double");reserved.add("else");
reserved.add("enum");reserved.add("extends");reserved.add("false");
reserved.add("final");reserved.add("finally");reserved.add("float");
reserved.add("for");reserved.add("if");reserved.add("goto");
reserved.add("implements");reserved.add("import");reserved.add("instanceof");
reserved.add("int");reserved.add("interface");reserved.add("long");
reserved.add("native");reserved.add("new");reserved.add("null");
reserved.add("package");reserved.add("private");reserved.add("protected");
reserved.add("public");reserved.add("return");reserved.add("short");
reserved.add("static");reserved.add("strictfp");reserved.add("super");
reserved.add("switch");reserved.add("synchronized");reserved.add("this");
reserved.add("throw");reserved.add("throws");reserved.add("transient");
reserved.add("true");reserved.add("try");reserved.add("void");
reserved.add("volatile");reserved.add("while");
}
/**
* Checks if the string that is provided is a valid Java package name (contains only
* [a-z,A-Z,_,$], every element is separated by a single '.' , an element can't be one of Java's
* reserved words.
*
* @param name The package name that needs to be validated.
* @return <b>true</b> if the package name is valid, <b>false</b> if its not valid.
*/
public static final boolean isValidPackageName(String name) {
String[] parts=name.split("\\.",-1);
for (String part:parts){
System.out.println(part);
if (reserved.contains(part)) return false;
if (!validPart(part)) return false;
}
return true;
}
/**
* Checks that a part (a word between dots) is a valid part to be used in a Java package name.
* @param part The part between dots (e.g. *PART*.*PART*.*PART*.*PART*).
* @return <b>true</b> if the part is valid, <b>false</b> if its not valid.
*/
private static boolean validPart(String part){
if (part==null || part.length()<1){
// Package part is null or empty !
return false;
}
if (Character.isJavaIdentifierStart(part.charAt(0))){
for (int i = 0; i < part.length(); i++){
char c = part.charAt(i);
if (!Character.isJavaIdentifierPart(c)){
// Package part contains invalid JavaIdentifier !
return false;
}
}
}else{
// Package part does not begin with a valid JavaIdentifier !
return false;
}
return true;
}
}
Укороченная версия рабочего регулярного выражения:
\p{Alnum}[\p{Alnum}._]+\p{Alnum}
Следующее выражение прекрасно работает для меня.
^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+$
Я скажу что-то вроде ([\w]+\.)*[\w]+
Но, может быть, я могу быть более конкретным, зная, что вы хотите с ним делать;)