Обнаружение переменных через строку

Я создаю простую среду IDE с помощью JTextPane и обнаруживаю ключевые слова и раскрашиваю их.

В настоящее время я могу обнаружить:

  • Комментарии
  • Строковые литералы
  • Целые числа и поплавки
  • Ключевые слова

Способ обнаружения этих типов осуществляется с помощью регулярных выражений.

Теперь я пытаюсь обнаружить переменные типа [int x= 10;] и окрашивать их в другой цвет.

В настоящее время я могу получить все типы данных, такие как int, float char, используя следующее regex:

Pattern words = Pattern.compile(\\bint\\b|\\bfloat\\b\\bchar\\b);
Matcher matcherWords = words.matcher(code);
while (matcherWords.find()) {
    System.out.print(code.substring(matcherWords.start(), matcherWords.end());
    // How to get next word that is a variable?
}

Ниже представлен пример моей программы:

enter image description here

Как я могу обнаружить переменные типа a, b, c после обнаружения int, float и т.д.

Ответ 1

Попробуйте следующее:

(?:(?<=int|float|String|double|char|long)(?:\s+[a-zA-Z_$][\w$]*\s*)|(?<=\G,)(?:\s*[a-zA-Z_$][\w$]*\s*))(?=,|;|=)

что означает:

  • (?<=int|float|String|double|char|long) - положительный lookbehind поиск типа переменной,
  • (?:\s+[a-zA-Z_$][\w$]*\s*) - не захватывающая группа: по крайней мере одно место, за которым следует действительная символы для переменных Java, за которыми следуют ноль или более пробелов.
  • | - или; альтернатива между именем обработки после var. тип или после запятой,
  • (?<=\G,) - положительный lookbehind для предыдущего совпадения и запятой (потому что другие части соответствуют пробелам с обеих сторон)
  • (?:\s*[a-zA-Z_$][\w$]*\s*) - не захватывающая группа: по крайней мере одно место, за которым следует действительная символы для переменных Java, за которыми следуют ноль или более пробелов.
  • (?=,|;|=) - положительный результат для запятой, знак равенства или полуколония

использует граничное соответствие \G (Конец предыдущего соответствия), поэтому альтернатива, в которой имена поиска между другими именами ( слова между пробелами или/и запятыми точно), будет соответствовать только в том случае, если это после предыдущего совпадения. Таким образом, он не будет соответствовать каждому слову между запятыми в Strings, например. Также я добавил $ в [a-zA-Z_$][\w$]*, поскольку разрешено в именах переменных, однако не рекомендуется.

DEMO

И для Java:

 Pattern pattern = Pattern.compile("(?:(?<=int|float|String|double|char|long)(?:\\s+[a-zA-Z_$][\\w$]*\\s*)|(?<=\\G,)(?:\\s*[a-zA-Z_$][\\w$]*\\s*))(?=,|;|=)");

ИЗМЕНИТЬ

Вы можете использовать (int |float |...) для сопоставления имен переменных напрямую с помощью matcher.start() и matcher.end() без пробелов, однако я бы предпочел использовать (?:\s*) в любом месте, где может быть место, а затем проверять избыточные пространства во время процесса обработки данных, потому что вы никогда не знаете, сколько пробелов будет вводиться пользователем (конечно, более одного избыточно, но оно все еще действует!).

Другим способом было бы сопоставить пробелы, но использовать группы, например:

(?:(?<=int|float|String|double|char|long)(?:\s+)([a-zA-Z_$][\w$]*)(?:\s*)|(?<=\G,)(?:\s*)([a-zA-Z_$][\w$]*)(?:\s*))(?=,|;|=)

DEMO

имена не имеют пробелов, но вам нужно извлечь их из групп 1 и 2 с помощью matcher.start(group no) и matcher.end(group no).

EDIT2 ответьте на вопрос из комментария

Это зависит от того, чего вы хотите достичь. Если вы просто хотите получить переменные как строки, достаточно использовать mathod trim(), но если вы хотите получить начальные и конечные индексы переменных в тексте, чтобы, например, выделить его в другом цвете, то лучше использовать его для пример matcher.start(1), чтобы извлечь начальный индекс группы 1. Рассмотрим этот пример:

import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern;

public class Test {
    public static void main(String[] args) throws IOException {
        String      text = "int a = 100;\n" +
                "float b = 100.10;\n" +
                "double c - 12.454545645;\n" +
                "long longest dsfsf = 453543543543;\n" +
                "a = d;\n" +
                "char     b = 'a';\n" +
                "String str = \"dfssffdsdfsd\"\n" +
                "int d,f,g;\n" +
                "int a,f,frhg = 0;\n" +
                "String string = \"a,b,c,d,e,f\"";

        Pattern pattern = Pattern.compile("(?:(?<=int|float|String|double|char|long)(?:\\s+)([a-zA-Z_$][\\w$]*)(?:\\s*)|(?<=\\G,)(?:\\s*)([a-zA-Z_$][\\w$]*)(?:\\s*))(?=,|;|=)");
        Matcher matcher = pattern.matcher(text);
        while(matcher.find()){
            System.out.println("trim(): " + text.substring(matcher.start(),matcher.end()).trim()); // cut off spaces by trim() method;

            int group = (matcher.group(1)==null)? 2 : 1; // check which group captured string;
            System.out.println("group(" + group + "): \n\t"  // to extract string by group capturing;
                    + text.substring(matcher.start(group),matcher.end(group))
                    + ",\n\tsubstring(" + matcher.start(group) + "," + matcher.end(group)+")");

        }
    }
}

на выходе присутствуют два совпадения.

Ответ 2

Вы пробовали шаблон lookbehind/lookahead?

Эта смехотворно длинная картина:

"(?<=int |float |String |double )([a-zA-Z_]\\w*)(?=,|;|\\s)|([a-zA-Z_]\\w*)(?=,|;|\\s*=)"

Возможность анализировать переменные и переменные, разделенные запятой.

public static void main(String[] args) throws Exception {
    String javaCode = "int a = 100;\n" + 
            "float b = 110;\n" + 
            "String c = \"Hello World\";" +
            "double d, e, f, g = 1.0, h;";

    Matcher matcher = Pattern
            .compile("(?<=int |float |String |double )([a-zA-Z_]\\w*)(?=,|;|\\s)|([a-zA-Z_]\\w*)(?=,|;|\\s*=)")
            .matcher(javaCode);
    while (matcher.find()) {
        System.out.println(matcher.group());
    }
}

Результаты:

a
b
c
d
e
f
g
h

Также протестировано здесь @regex101

Ответ 3

\b(?:int|float|String|char|double|long)\b\s+([^=;]+)

Вы пытались сопоставить только имя переменной? Если да, то это поможет.