Regex per rimuovere i commenti della riga da C #

Sto lavorando a una routine per rimuovere i commenti di blocco o linea da un codice C #. Ho esaminato gli altri esempi sul sito, ma non ho trovato la risposta esatta che sto cercando.

Posso associare i commenti di blocco (/ * commento * /) nella loro interezza usando questa espressione regolare con RegexOptions.Singleline:

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

E posso abbinare i commenti di riga (// commento) nella loro interezza usando questa espressione regolare con RegexOptions.Multiline:

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

Nota: sto usando [^\r\n] invece di $ perché $ include anche \r nella partita.

Tuttavia, questo non funziona nel modo in cui lo voglio.

Ecco il mio codice di test con cui sto confrontando:

 // 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"; 

L’espressione di blocco corrisponde

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

che va bene e bene, ma l’espressione di linea corrisponde

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

e

 // do not remove nested comments 

Inoltre, se non ho il segno di spunta * / positivo nell’espressione di linea due volte, corrisponde

 // do not remove nested comments * 

che davvero non voglio

Quello che voglio è un’espressione che abbinerà i caratteri, iniziando con // , alla fine della riga, ma non contiene */ tra // e la fine della riga.

Inoltre, solo per soddisfare la mia curiosità, qualcuno può spiegare perché ho bisogno del lookahead due volte? (//((?!\*/).)*)[^\r\n] e (//(.)*)(?!\*/)[^\r\n] includeranno entrambi * , ma (//((?!\*/).)*)(?!\*/)[^\r\n] (//((?!\*/).)*(?!\*/))[^\r\n] (//((?!\*/).)*)(?!\*/)[^\r\n] e (//((?!\*/).)*(?!\*/))[^\r\n] non lo farà.

Entrambe le tue espressioni regolari (per i commenti di blocco e di linea) hanno bug. Se vuoi posso descrivere i bug, ma ho sentito che è forse più produttivo se ne scrivo di nuovi, soprattutto perché intendo scrivere uno solo che corrisponda a entrambi.

Il fatto è che ogni volta che le stringhe /* e // e letterali “interferiscono” l’una con l’altra, è sempre quella che inizia per prima che ha la precedenza. È molto comodo perché è esattamente come funzionano le espressioni regolari: trova prima la prima partita.

Quindi definiamo un’espressione regolare che corrisponda a ciascuno di questi quattro token:

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

Per rispondere alla domanda nel titolo (commenti sulle strisce), dobbiamo:

  • Sostituisci i commenti del blocco con niente
  • Sostituisci i commenti di riga con una nuova riga (perché la regex mangia la nuova riga)
  • Mantieni le stringhe letterali dove sono.

Regex.Replace può farlo facilmente usando una funzione 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); 

Ho eseguito questo codice su tutti gli esempi forniti da Holystream e su vari altri casi a cui potevo pensare, e funziona come un incantesimo. Se è ansible fornire un esempio in cui non riesce, sono felice di modificare il codice per te.

Prima di implementare questo, è necessario creare prima i casi di test

  1. Commenti semplici / * * /, //, ///
  2. Commenti a più righe / * Questo \ nis \ na \ ntest * /
  3. Commenti dopo la riga di codice var a = “apple”; // test o / * test * /
  4. Commenti all’interno di commenti / * Questo // è un test /, o // Questo / è un test * /
  5. Semplici commenti non commenti che assomigliano a commenti e appaiono tra virgolette var comment = “/ * Questo è un test * /”, oppure var url = ” http://stackoverflow.com “;
  6. I commenti non complessi sembrano commenti: var abc = @ “this / * \ n è un commento in quote \ n * /”, con o senza spazi tra “e / * o * / e”

Ci sono probabilmente più casi là fuori.

Dopo averli tutti, è ansible creare una regola di analisi per ognuno di essi o raggrupparne alcuni.

Risolvere questo con la sola espressione regolare probabilmente sarà molto difficile e sobject a errori, difficile da testare e difficile da mantenere da parte tua e di altri programmatori.

Potresti tokenize il codice con un’espressione come:

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

Corrisponde anche a qualche escape / struttura non valido (ad esempio 'foo' ), ma probabilmente corrisponderà a tutti i token di interesse validi (a meno che non abbia dimenticato qualcosa), quindi funziona bene per un codice valido.

Usarlo in una sostituzione e catturare le parti che vuoi mantenere ti darà il risultato desiderato. Vale a dire:

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

Esempio di app :

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

Produzione:

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

Ho trovato questo su http://gskinner.com/RegExr/ (denominato “.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%]*)\>) 

Quando lo provo sembra che rimuova tutti i // commenti e / * commenti * / come dovrebbe, lasciando quelli dietro le virgolette.

Non ho provato molto, ma sembra funzionare abbastanza bene (anche se è una orribile linea mostruosa di regex).

Vedi anche il mio progetto per la minificazione del codice C #: CSharp-Minifier

A parte la rimozione di commenti, spazi e interruzioni di riga dal codice, al momento è in grado di comprimere nomi di variabili locali e fare altri minifici.

per blocco Commenti (/ * … * /) puoi usare questo exp:

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

funzionerà anche con commenti multilinea.