Java: expresiones regulares que encuentran comentarios en el código

Un poco de diversión con Java esta vez. Quiero escribir un progtwig que lea un código de entrada estándar (línea por línea, por ejemplo), como:

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

encuentra todos los comentarios en él y los elimina. Intento usar expresiones regulares, y por ahora he hecho algo como esto:

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

pero parece no funcionar para todos los casos, por ejemplo:

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

¿Algún consejo o idea diferente de regex? Gracias por adelantado.

Puede que ya hayas renunciado a esto, pero me intrigó el problema.

Creo que esta es una solución parcial …

Regex nativo:

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

En Java:

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

Esto parece manejar adecuadamente los comentarios incrustados en cadenas, así como las comillas escapadas correctamente dentro de las cadenas. Tiré algunas cosas para verificar pero no exhaustivamente.

Hay un compromiso en que todos los “” bloques en el código terminarán con espacio después de ellos. Mantener esto simple y resolver ese problema sería muy difícil dada la necesidad de manejarlo limpiamente:

 int/* some comment */foo = 5; 

Un simple bucle Matcher.find / appendReplacement podría verificar condicionalmente el grupo (1) antes de reemplazarlo con un espacio y solo sería un puñado de líneas de código. Aún más simple que un analizador completo tal vez. (Podría agregar el bucle matcher también si alguien está interesado.)

El último ejemplo no es problema, creo:

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

… porque el comentario en realidad termina con "We can use */ . Este código no se comstack.

Pero tengo otro caso problemático:

 int/*comment*/foo=3; 

Su patrón transformará esto en:

 intfoo=3; 

… qué es código inválido Así que es mejor que reemplace sus comentarios con " " lugar de "" .

Creo que una solución 100% correcta usando expresiones regulares es inhumana o imposible (teniendo en cuenta escapes, etc.).

Creo que la mejor opción sería usar ANTLR, creo que incluso proporcionan una gramática de Java que puedes usar.

Terminé con esta solución.

 public class CommentsFun { static List commentMatches = new ArrayList(); 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 commentsToRemove = new ArrayList(); 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; } } 

Otra alternativa es usar alguna biblioteca que soporte el análisis AST, por ejemplo, org.eclipse.jdt.core tiene todas las API que necesita para hacer esto y más. Pero esa es solo una alternativa 🙂