Подтвердить что ты не робот

Регулярное выражение, соответствующее полностью квалифицированным именам классов

Каков наилучший способ сопоставления полного имени класса Java в тексте?

Примеры: java.lang.Reflect, java.util.ArrayList, org.hibernate.Hibernate.

4b9b3361

Ответ 1

Полностью квалифицированное имя класса 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

Ответ 2

Вот полноценный рабочий класс с тестами, основанный на отличном комментарии @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"));
    }
}

Ответ 3

Образец, предоставленный Рено, работает. Но, насколько я могу судить, он всегда будет отступать в конце.

Чтобы оптимизировать его, вы можете поменять первую половину с последним. Обратите внимание на точечное совпадение, которое также необходимо изменить.

Ниже приведена моя версия, которая по сравнению с оригиналом работает примерно в два раза быстрее:

String ID_PATTERN = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*";
Pattern FQCN = Pattern.compile(ID_PATTERN + "(\\." + ID_PATTERN + ")*");

Я не могу писать комментарии, поэтому вместо этого я решил написать ответ.

Ответ 4

Я пришел (сам по себе) к аналогичному ответу (как ответ Томалака), что-то вроде M.M.M.N:

([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 для букв и цифр (он не включает символ подчеркивания (_))

Надеюсь на эту помощь.

Ответ 5

Следующее выражение прекрасно работает для меня.

^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+$

Ответ 6

Следующий класс проверяет правильность имени предоставленного пакета:

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

Ответ 7

более короткая версия рабочего регулярного выражения:

\p{Alnum}[\p{Alnum}._]+\p{Alnum}

Ответ 8

Я скажу что-то вроде ([\w]+\.)*[\w]+

Но, может быть, я могу быть более конкретным, зная, что вы хотите с ним делать;)