Java - регулярное выражение, содержащее комментарии в коде

Немного весело с Java на этот раз. Я хочу написать программу, которая считывает код со стандартного ввода (например, строка за строкой), например:

// some comment
class Main {
    /* blah */
    // /* foo
    foo();
    // foo */
    foo2();
    /* // foo2 */
}

находит в нем все комментарии и удаляет их. Я пытаюсь использовать регулярные выражения, и теперь я сделал что-то вроде этого:

private static String ParseCode(String pCode)
{
    String MyCommentsRegex = "(?://.*)|(/\\*(?:.|[\\n\\r])*?\\*/)";
    return pCode.replaceAll(MyCommentsRegex, " ");
}

но, похоже, не работает для всех случаев, например:

System.out.print("We can use /* comments */ inside a string of course, but it shouldn't start a comment");

Любые советы или идеи, отличные от регулярных выражений? Спасибо заранее.

Ответы

Ответ 1

Возможно, вы уже отказались от этого, но я был заинтригован этой проблемой.

Я считаю, что это частичное решение...

Внутреннее регулярное выражение:

//.*|("(?:\\[^"]|\\"|.)*?")|(?s)/\*.*?\*/

В Java:

String clean = original.replaceAll( "//.*|(\"(?:\\\\[^\"]|\\\\\"|.)*?\")|(?s)/\\*.*?\\*/", "$1 " );

Это, как представляется, правильно обрабатывает комментарии, встроенные в строки, а также правильно экранированные кавычки внутри строк. Я бросил на него несколько вещей, чтобы проверить, но не исчерпывающе.

Существует один компромисс в том, что все "" блоки в коде будут содержать пробел после них. Сохранение этой простой и решения этой проблемы было бы очень затруднительным, учитывая необходимость чистой обработки:

int/* some comment */foo = 5;

Простой цикл Matcher.find/appendReplacement может условно проверять группу (1) перед заменой пробелом и будет всего лишь несколькими строками кода. Возможно, еще проще, чем полный парсер. (Я мог бы добавить петлю сопряжения, если кто-то заинтересован.)

Ответ 2

Последний пример не проблема, я думаю:

/* we comment out some code
System.out.print("We can use */ inside a string of course");
we end the comment */

... потому что комментарий на самом деле заканчивается на "We can use */. Этот код не компилируется.

Но у меня есть еще один проблемный случай:

int/*comment*/foo=3;

Ваш шаблон преобразует это в:

intfoo=3;

... неверный код. Поэтому лучше замените свои комментарии " " вместо "".

Ответ 3

Я думаю, что 100% -ное правильное решение с использованием регулярных выражений является либо бесчеловечным, либо невозможным (с учетом побегов и т.д.).

Я считаю, что лучшим вариантом будет использование ANTLR - я считаю, что они даже предоставляют грамматику Java, которую вы можете использовать.

Ответ 4

Я закончил с этим решением.

public class CommentsFun {
    static List<Match> commentMatches = new ArrayList<Match>();

    public static void main(String[] args) {
        Pattern commentsPattern = Pattern.compile("(//.*?$)|(/\\*.*?\\*/)", Pattern.MULTILINE | Pattern.DOTALL);
        Pattern stringsPattern = Pattern.compile("(\".*?(?<!\\\\)\")");

        String text = getTextFromFile("src/my/test/CommentsFun.java");

        Matcher commentsMatcher = commentsPattern.matcher(text);
        while (commentsMatcher.find()) {
            Match match = new Match();
            match.start = commentsMatcher.start();
            match.text = commentsMatcher.group();
            commentMatches.add(match);
        }

        List<Match> commentsToRemove = new ArrayList<Match>();

        Matcher stringsMatcher = stringsPattern.matcher(text);
        while (stringsMatcher.find()) {
            for (Match comment : commentMatches) {
                if (comment.start > stringsMatcher.start() && comment.start < stringsMatcher.end())
                    commentsToRemove.add(comment);
            }
        }
        for (Match comment : commentsToRemove)
            commentMatches.remove(comment);

        for (Match comment : commentMatches)
            text = text.replace(comment.text, " ");

        System.out.println(text);
    }

    //Single-line

    // "String? Nope"

    /*
    * "This  is not String either"
    */

    //Complex */
    ///*More complex*/

    /*Single line, but */

    String moreFun = " /* comment? doubt that */";

    String evenMoreFun = " // comment? doubt that ";

    static class Match {
        int start;
        String text;
    }
}

Ответ 5

Другой альтернативой является использование некоторой библиотеки, поддерживающей разбор АСТ. org.eclipse.jdt.core имеет все API-интерфейсы, необходимые для этого и многое другое. Но тогда это только одна альтернатива:)