2010-08-19 2 views
39

Я работаю над подпрограммой, чтобы разделить блок или комментарии линии от некоторого кода C#. Я просмотрел другие примеры на сайте, но не нашел точный ответ, который я ищу.Regex to strip line comments from C#

я могу соответствовать блочные комментарии (/ * комментарий * /) в полном объеме, используя это регулярное выражение с RegexOptions.Singleline:

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

И я могу соответствовать комментарии строки (комментарий), // в их цельность, используя это регулярное выражение с RegexOptions.Multiline:

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

Примечание: Я использую [^\r\n] вместо $, потому что $ в том числе \r в матче тоже.

Однако, это не довольно работать так, как я этого хочу.

Вот мой тестовый код, который я соответствие с:

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

Выражение блок соответствует

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

, который прекрасно и хорошо, но выражение линия соответствует

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

и

// do not remove nested comments 

Кроме того, если у меня нет */положительный предпросмотр в выражении линии дважды, он соответствует

// do not remove nested comments * 

который я действительно не хочу.

То, что я хочу, это выражение, которое будет соответствовать символы, начиная с //, до конца строки, но делает не содержат */ между // и концом строки.

Кроме того, чтобы удовлетворить мое любопытство, может ли кто-нибудь объяснить, почему мне нужен взгляд дважды? (//((?!\*/).)*)[^\r\n] и (//(.)*)(?!\*/)[^\r\n] оба будут включать *, но (//((?!\*/).)*)(?!\*/)[^\r\n] и (//((?!\*/).)*(?!\*/))[^\r\n] не будут.

+3

Вы также рассмотрели случай, когда 'string foo =" http://stackoverflow.com; "' –

+1

Ваши шаблоны '/ * ... * /' из-за жадности, например. рассмотрим '/ * комментарий1 */not-a-comment!/* комментарий2 */'. – polygenelubricants

+0

Возможно, вы можете использовать синтаксический анализатор для C#: http://stackoverflow.com/questions/81406/parser-for-c – TrueWill

ответ

73

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

Дело в том, что каждый раз, когда у вас есть /* и // и буквальные строки, «мешающие» друг другу, это всегда тот, который начинается первым, что имеет преимущество. Это очень удобно, потому что именно так работают регулярные выражения: сначала найдите первое совпадение.

Так давайте определим регулярное выражение, которое соответствует каждому из этих четырех маркеров:

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

Чтобы ответить на этот вопрос в названии (полоса комментариев), нам нужно:

  • Заменить блок комментарии ни с чем
  • Замените комментарий строки символом новой строки (потому что регулярное выражение ест новую строку)
  • Храните литературные строки там, где они есть.

Regex.Replace может легко сделать это с помощью функции 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); 

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

+0

Мне не нужно извлекать комментарии, просто удалите их из исходного сценария. Я пробовал ваш код, и он работал хорошо. В идеале, я хотел бы удалить любую строку полностью, если строка содержит только комментарии. например пустые строки остались там, где был комментарий. Однако это не требование, а только предпочтение форматирования. Благодарю. –

+2

@Welton: Ну, вы можете просто запустить 'Regex.Replace (@"^(\ s * \ r? \ N) {2,} ", Environment.Newline, RegexOptions.Multiline)' на результат потом, но это удалит пустые двойные строки, которые * не * оставили комментарий в нем. – Timwi

+0

Я видел, что вы протестировали это: http://csharp.pastebin.com/0aqBdFE5 , но когда у вас есть что-то вроде этого: string input = "1 + 2 // comments"; он терпит неудачу, он дает вам результат «1 + 2 \ r \ n» из-за Environment.Newline в тройном операторе – juFo

7

Перед тем, как осуществить это, вам нужно будет создать тестовые случаи для его первого

  1. Простые комментарии/* * /, //, ///
  2. Мульти комментарии линия/* Это \ Nis \ na \ ntest */
  3. Комментарии после строки кода var a = "apple"; // тест или/* тест */
  4. Комментариев по комментариям/* Это // тест /или // Это/ является тест */
  5. Простых не являющиеся комментариями, которые выглядят как комментарии, и появляется в кавычках var comment = "/ * Это тест * /", или var url = "http://stackoverflow.com";
  6. комплекс входят комментарии TAHT выглядеть комментарии: вар а = @ «это/* \ п комментарий в кавычках \ п * /», с или без пробелов между „и/* или */и“

Есть, вероятно, больше случаев.

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

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

+0

Holystream, у меня есть некоторые из тестовых случаев, которые вы упомянули, но не все. Мой пример выше охватывает 1 (частично), 2, 3 и 4. 5 и 6 - хорошие моменты, которые я не рассматривал. –

+0

Holystream, я считаю, вы делаете это сложнее, чем есть. Совпадение двух стилей комментариев очень просто с регулярными выражениями - на самом деле, лексир C# (и C++), вероятно, делает это. Это контрастирует с чем-то вроде HTML, который трудно сопоставить с регулярными выражениями, поскольку теги HTML могут входить и потому, что они входят в слишком много разных разновидностей. – Timwi

+0

@Timwi: На самом деле .NET использует лексический анализатор. Символы комментариев - это всего лишь токены. Http: //en.wikipedia.org/wiki/Lexical_analysis – chilltemp

4

Вы можете разметить код с выражением, как: (., Например, 'foo')

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

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

Использование в замене и захвате деталей, которые вы хотите сохранить, даст вам желаемый результат. То есть:

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

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

Выход:

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

Я попробую. Благодарю. –

+1

Подождите, почему я ответил на это через 2 года после того, как его спросили, ответили и приняли? Давать практически тот же ответ? Как он появился в моем списке? Должно быть, была какая-то ошибка или что-то еще, я не делаю таких вещей. (lol) – Qtax

+0

Я нашел, что это идеальный ответ для меня (C#), однако регулярное выражение не работает на javascript. –

1

Я нашел это в http://gskinner.com/RegExr/ (под названием ".Net Комментарии 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%]*)\>) 

Когда я его тестирую, он, кажется, удаляет все // комментарии и/* комментарии * /, как следует, оставляя эти внутренние кавычки позади.

Не испытал его много, но, похоже, работает очень хорошо (хотя его ужасная чудовищная линия регулярного выражения).

+0

Ok .. после некоторого тестирования я заметил, что есть проблемы с комментариями, содержащими знак минус (-) и несколько многострочных комментариев (/ * комментарий */не комментарий/* комментарий снова * /). Но если кто-то позаботится об этом, я думаю, что это довольно хорошее решение. – einord

+0

для меня это нормально работает:) – 99999

0

для блока Комментариев (/ * ... * /), вы можете использовать этот ехр:

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

он будет работать с многострочными комментариями тоже.

0

Также см мой проект C# код минификация: CSharp-Minifier

Помимо удаления комментариев, пробелов и разрыва строки из кода, в настоящее время в состоянии сжать имена локальных переменных и сделать еще minifications.