Regex para despojar los comentarios de línea de C #

Estoy trabajando en una rutina para quitar los comentarios de bloque o línea de algún código C #. He analizado otros ejemplos en el sitio, pero no he encontrado la respuesta exacta que estoy buscando.

Puedo hacer coincidir los comentarios de bloque (/ * comment * /) en su totalidad usando esta expresión regular con RegexOptions.Singleline:

(/\*[\w\W]*\*/)

Y puedo hacer coincidir los comentarios de línea (// comentario) en su totalidad usando esta expresión regular con RegexOptions.Multiline:

(//((?!\*/).)*)(?!\*/)[^\r\n]

Nota: Estoy usando [^\r\n] lugar de $ porque $ incluye \r en la coincidencia, también.

Sin embargo, esto no funciona del modo que yo quiero.

Aquí está mi código de prueba con el que me estoy uniendo:

 // remove whole line comments bool broken = false; // remove partial line comments if (broken == true) { return "BROKEN"; } /* remove block comments else { return "FIXED"; } // do not remove nested comments */ bool working = !broken; return "NO COMMENT"; 

La expresión de bloque coincide

 /* remove block comments else { return "FIXED"; } // do not remove nested comments */ 

que está bien y bien, pero la expresión de línea coincide

 // remove whole line comments // remove partial line comments 

y

 // do not remove nested comments 

Además, si no tengo el * / positive lookahead en la expresión de línea dos veces, coincide

 // do not remove nested comments * 

que realmente no quiero

Lo que quiero es una expresión que coincida con los caracteres, comenzando por // , hasta el final de la línea, pero que no contiene */ entre // y el final de la línea.

Además, solo para satisfacer mi curiosidad, ¿alguien puede explicar por qué necesito mirar hacia el futuro dos veces? (//((?!\*/).)*)[^\r\n] y (//(.)*)(?!\*/)[^\r\n] ambos incluirán * , pero (//((?!\*/).)*)(?!\*/)[^\r\n] y (//((?!\*/).)*(?!\*/))[^\r\n] no.

Ambas expresiones regulares (para comentarios de bloque y línea) tienen errores. Si quieres, puedo describir los errores, pero creo que es quizás más productivo si escribo nuevos, especialmente porque tengo la intención de escribir uno solo que coincida con ambos.

El caso es que, cada vez que tiene /* y // y cadenas literales “interfiriendo” entre sí, siempre es el que comienza primero el que tiene prioridad. Eso es muy conveniente porque así es exactamente cómo funcionan las expresiones regulares: primero encuentra la primera coincidencia.

Así que vamos a definir una expresión regular que coincida con cada uno de esos cuatro tokens:

 var blockComments = @"/\*(.*?)\*/"; var lineComments = @"//(.*?)\r?\n"; var strings = @"""((\\[^\n]|[^""\n])*)"""; var verbatimStrings = @"@(""[^""]*"")+"; 

Para responder la pregunta en el título (comentarios de la tira), tenemos que:

  • Reemplazar los comentarios del bloque con nada
  • Reemplace los comentarios de línea con una nueva línea (porque la expresión regular se come la nueva línea)
  • Mantenga las cuerdas literales donde están.

Regex.Replace puede hacer esto fácilmente usando una función MatchEvaluator:

 string noComments = Regex.Replace(input, blockComments + "|" + lineComments + "|" + strings + "|" + verbatimStrings, me => { if (me.Value.StartsWith("/*") || me.Value.StartsWith("//")) return me.Value.StartsWith("//") ? Environment.NewLine : ""; // Keep the literal strings return me.Value; }, RegexOptions.Singleline); 

Ejecuté este código en todos los ejemplos que me proporcionó Holystream y en otros casos en los que pude pensar, y funciona como un hechizo. Si puede proporcionar un ejemplo en el que falla, me complace ajustar el código por usted.

Antes de implementar esto, deberá crear casos de prueba primero

  1. Comentarios simples / * * /, //, ///
  2. Comentarios de líneas múltiples / * This \ nis \ na \ ntest * /
  3. Comentarios después de la línea de código var a = “apple”; // prueba o / * prueba * /
  4. Comentarios dentro de los comentarios / * Este // es un test /, o // Este / es un test * /
  5. Comentarios simples que parecen comentarios y aparecen entre comillas var comment = “/ * Esto es una prueba * /”, o var url = ” http://stackoverflow.com “;
  6. Los comentarios complejos no se parecen a los comentarios: var abc = @ “this / * \ n es un comentario entre comillas \ n * /”, con o sin espacios entre “y / o * / y”

Probablemente haya más casos por ahí.

Una vez que los tiene todos, puede crear una regla de análisis para cada uno de ellos, o agrupar algunos de ellos.

Resolver esto solo con expresiones regulares probablemente sea muy difícil y propenso a errores, difícil de probar y difícil de mantener por usted y otros progtwigdores.

Podría tokenizar el código con una expresión como:

 @(?:"[^"]*")+|"(?:[^"\n\\]+|\\.)*"|'(?:[^'\n\\]+|\\.)*'|//.*|/\*(?s:.*?)\*/ 

También coincidiría con algunas escapes / estructuras no válidas (por ejemplo, 'foo' ), pero probablemente coincidirá con todas las fichas de interés válidas (a menos que se me haya olvidado algo), lo que funcionaría bien para código válido.

Usarlo en un reemplazo y capturar las partes que desea conservar le dará el resultado deseado. Es decir:

 static string StripComments(string code) { var re = @"(@(?:""[^""]*"")+|""(?:[^""\n\\]+|\\.)*""|'(?:[^'\n\\]+|\\.)*')|//.*|/\*(?s:.*?)\*/"; return Regex.Replace(code, re, "$1"); } 

Ejemplo de aplicación :

 using System; using System.Text.RegularExpressions; namespace Regex01 { class Program { static string StripComments(string code) { var re = @"(@(?:""[^""]*"")+|""(?:[^""\n\\]+|\\.)*""|'(?:[^'\n\\]+|\\.)*')|//.*|/\*(?s:.*?)\*/"; return Regex.Replace(code, re, "$1"); } static void Main(string[] args) { var input = "hello /* world */ oh \" '\\\" // ha/*i*/\" and // bai"; Console.WriteLine(input); var noComments = StripComments(input); Console.WriteLine(noComments); } } } 

Salida:

 hello /* world */ oh " '\" // ha/*i*/" and // bai hello oh " '\" // ha/*i*/" and 

Encontré este en http://gskinner.com/RegExr/ (llamado “.Net Comments aspx”)

 (//[\t|\s|\w|\d|\.]*[\r\n|\n])|([\s|\t]*/\*[\t|\s|\w|\W|\d|\.|\r|\n]*\*/)|(\<[!%][ \r\n\t]*(--([^\-]|[\r\n]|-[^\-])*--[ \r\n\t%]*)\>) 

Cuando lo pruebo, parece eliminar todos los // comentarios y / * comentarios * / como debería, dejando atrás las comillas internas.

No lo he probado mucho, pero parece funcionar bastante bien (a pesar de que es una horrible línea monstruosa de expresiones regulares).

También vea mi proyecto para la minificación del código C #: CSharp-Minifier

Además de quitar comentarios, espacios y saltos de línea del código, en este momento es capaz de comprimir nombres de variables locales y hacer otras minificaciones.

para comentarios de bloque (/ * … * /) puede usar este exp:

/\*([^\*/])*\*/

también funcionará con comentarios de líneas múltiples.