Регулярное выражение для сопоставления всех комментариев в скрипте T-SQL - PullRequest
9 голосов
/ 07 октября 2011

Мне нужно регулярное выражение, чтобы захватить ВСЕ комментарии в блоке T-SQL. Выражение должно работать с классом .Net Regex.

Допустим, у меня есть следующий T-SQL:

-- This is Comment 1
SELECT Foo FROM Bar
GO

-- This is
-- Comment 2
UPDATE Bar SET Foo == 'Foo'
GO

/* This is Comment 3 */
DELETE FROM Bar WHERE Foo = 'Foo'

/* This is a
multi-line comment */
DROP TABLE Bar

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

РЕДАКТИРОВАТЬ: Для той же цели было бы иметь выражение, которое принимает все, но комментарии.

Ответы [ 7 ]

17 голосов
/ 07 октября 2011

Это должно работать:

(--.*)|(((/\*)+?[\w\W]+?(\*/)+))
9 голосов
/ 11 декабря 2012

В PHP я использую этот код для раскомментирования SQL (это прокомментированная версия -> модификатор x):

trim( preg_replace( '@
(([\'"]).*?[^\\\]\2) # $1 : Skip single & double quoted expressions
|(                   # $3 : Match comments
    (?:\#|--).*?$    # - Single line comment
    |                # - Multi line (nested) comments
     /\*             #   . comment open marker
        (?: [^/*]    #   . non comment-marker characters
            |/(?!\*) #   . not a comment open
            |\*(?!/) #   . not a comment close
            |(?R)    #   . recursive case
        )*           #   . repeat eventually
    \*\/             #   . comment close marker
)\s*                 # Trim after comments
|(?<=;)\s+           # Trim after semi-colon
@msx', '$1', $sql ) );

Короткая версия:

trim( preg_replace( '@(([\'"]).*?[^\\\]\2)|((?:\#|--).*?$|/\*(?:[^/*]|/(?!\*)|\*(?!/)|(?R))*\*\/)\s*|(?<=;)\s+@ms', '$1', $sql ) );
5 голосов
/ 07 октября 2011

Используя этот код:

StringCollection resultList = new StringCollection(); 
try {
Regex regexObj = new Regex(@"/\*(?>(?:(?!\*/|/\*).)*)(?>(?:/\*(?>(?:(?!\*/|/\*).)*)\*/(?>(?:(?!\*/|/\*).)*))*).*?\*/|--.*?\r?[\n]", RegexOptions.Singleline);
Match matchResult = regexObj.Match(subjectString);
while (matchResult.Success) {
    resultList.Add(matchResult.Value);
    matchResult = matchResult.NextMatch();
} 
} catch (ArgumentException ex) {
// Syntax error in the regular expression
}

Со следующим вводом:

-- This is Comment 1
SELECT Foo FROM Bar
GO

-- This is
-- Comment 2
UPDATE Bar SET Foo == 'Foo'
GO

/* This is Comment 3 */
DELETE FROM Bar WHERE Foo = 'Foo'

/* This is a
multi-line comment */
DROP TABLE Bar

/* comment /* nesting */ of /* two */ levels supported */
foo...

Создает эти совпадения:

-- This is Comment 1
-- This is
-- Comment 2
/* This is Comment 3 */
/* This is a
multi-line comment */
/* comment /* nesting */ of /* two */ levels supported */

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

3 голосов
/ 27 ноября 2015

Я сделал эту функцию, которая удаляет все комментарии SQL, , используя простые регулярные выражения .Он удаляет как комментарии строки (даже если после строки нет разрыва строки), так и комментарии блока (даже если есть вложенные комментарии блока).Эта функция также может заменять литералы (полезно, если вы ищете что-то внутри процедур SQL, но хотите игнорировать строки).

Мой код основан на этом ответе (что касается комментариев C #), поэтому мне пришлось изменить строковые комментарии с "//" на "-", но, что более важно, мне пришлось переписать регулярное выражение блочных комментариев (используя балансировочные группы ), потому что SQL допускает комментарии вложенных блоков , а C # - нет.

Кроме того, у меня есть аргумент " preservePositions ", который вместо удаления комментариев просто заполняет комментарии пробелами.Это полезно, если вы хотите сохранить исходное положение каждой команды SQL, если вам нужно манипулировать исходным сценарием, сохраняя исходные комментарии.

Regex everythingExceptNewLines = new Regex("[^\r\n]");
public string RemoveComments(string input, bool preservePositions, bool removeLiterals=false)
{
    //based on /2737212/regex-chtoby-ubrat-kommentarii-stroki-iz-c#2737222

    var lineComments = @"--(.*?)\r?\n";
    var lineCommentsOnLastLine = @"--(.*?)$"; // because it's possible that there's no \r\n after the last line comment
    // literals ('literals'), bracketedIdentifiers ([object]) and quotedIdentifiers ("object"), they follow the same structure:
    // there's the start character, any consecutive pairs of closing characters are considered part of the literal/identifier, and then comes the closing character
    var literals = @"('(('')|[^'])*')"; // 'John', 'O''malley''s', etc
    var bracketedIdentifiers = @"\[((\]\])|[^\]])* \]"; // [object], [ % object]] ], etc
    var quotedIdentifiers = @"(\""((\""\"")|[^""])*\"")"; // "object", "object[]", etc - when QUOTED_IDENTIFIER is set to ON, they are identifiers, else they are literals
    //var blockComments = @"/\*(.*?)\*/";  //the original code was for C#, but Microsoft SQL allows a nested block comments // //https://msdn.microsoft.com/en-us/library/ms178623.aspx
    //so we should use balancing groups // http://weblogs.asp.net/whaggard/377025
    var nestedBlockComments = @"/\*
                                (?>
                                /\*  (?<LEVEL>)      # On opening push level
                                | 
                                \*/ (?<-LEVEL>)     # On closing pop level
                                |
                                (?! /\* | \*/ ) . # Match any char unless the opening and closing strings   
                                )+                         # /* or */ in the lookahead string
                                (?(LEVEL)(?!))             # If level exists then fail
                                \*/";

    string noComments = Regex.Replace(input,
            nestedBlockComments + "|" + lineComments + "|" + lineCommentsOnLastLine + "|" + literals + "|" + bracketedIdentifiers + "|" + quotedIdentifiers,
        me => {
            if (me.Value.StartsWith("/*") && preservePositions)
                return everythingExceptNewLines.Replace(me.Value, " "); // preserve positions and keep line-breaks // return new string(' ', me.Value.Length);
            else if (me.Value.StartsWith("/*") && !preservePositions)
                return "";
            else if (me.Value.StartsWith("--") && preservePositions)
                return everythingExceptNewLines.Replace(me.Value, " "); // preserve positions and keep line-breaks
            else if (me.Value.StartsWith("--") && !preservePositions)
                return everythingExceptNewLines.Replace(me.Value, ""); // preserve only line-breaks // Environment.NewLine;
            else if (me.Value.StartsWith("[") || me.Value.StartsWith("\""))
                return me.Value; // do not remove object identifiers ever
            else if (!removeLiterals) // Keep the literal strings
                return me.Value;
            else if (removeLiterals && preservePositions) // remove literals, but preserving positions and line-breaks
            {
                var literalWithLineBreaks = everythingExceptNewLines.Replace(me.Value, " ");
                return "'" + literalWithLineBreaks.Substring(1, literalWithLineBreaks.Length - 2) + "'";
            }
            else if (removeLiterals && !preservePositions) // wrap completely all literals
                return "''";
            else
                throw new NotImplementedException();
        },
        RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
    return noComments;
}

Тест 1 (сначала оригинальный, затем удаление комментариев, последнее удалениекомментарии / литералы)

[select /* block comment */ top 1 'a' /* block comment /* nested block comment */*/ from  sys.tables --LineComment
union
select top 1 '/* literal with */-- lots of comments symbols' from sys.tables --FinalLineComment]

[select                     top 1 'a'                                               from  sys.tables              
union
select top 1 '/* literal with */-- lots of comments symbols' from sys.tables                   ]

[select                     top 1 ' '                                               from  sys.tables              
union
select top 1 '                                             ' from sys.tables                   ]

тест 2 (сначала оригинал, затем удаление комментариев, последнее удаление комментариев / литералов)

Original:
[create table [/*] /* 
  -- huh? */
(
    "--
     --" integer identity, -- /*
    [*/] varchar(20) /* -- */
         default '*/ /* -- */' /* /* /* */ */ */
);
            go]


[create table [/*]    

(
    "--
     --" integer identity,      
    [*/] varchar(20)         
         default '*/ /* -- */'                  
);
            go]


[create table [/*]    

(
    "--
     --" integer identity,      
    [*/] varchar(20)         
         default '           '                  
);
            go]
1 голос
/ 18 марта 2016

Следующее работает нормально - pg-minify , и не только для PostgreSQL, но и для MS-SQL.

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

Эта библиотека удаляет все комментарии как часть минимизации скрипта.

1 голос
/ 23 апреля 2014

Я вижу, что вы используете Microsoft SQL Server (в отличие от Oracle или MySQL). Если вы ослабите требование регулярных выражений, теперь возможно (с 2012 года) использовать собственный синтаксический анализатор Microsoft:

using Microsoft.SqlServer.Management.TransactSql.ScriptDom;

...

public string StripCommentsFromSQL( string SQL ) {

    TSql110Parser parser = new TSql110Parser( true );
    IList<ParseError> errors;
    var fragments = parser.Parse( new System.IO.StringReader( SQL ), out errors );

    // clear comments
    string result = string.Join ( 
      string.Empty,
      fragments.ScriptTokenStream
          .Where( x => x.TokenType != TSqlTokenType.MultilineComment )
          .Where( x => x.TokenType != TSqlTokenType.SingleLineComment )
          .Select( x => x.Text ) );

    return result;

}

См. Удаление комментариев из SQL

1 голос
/ 04 декабря 2011

Это работает для меня:

(/\*(.|[\r\n])*?\*/)|(--(.*|[\r\n]))

Соответствует всем комментариям, начинающимся с - или заключенным в * / .. * / blocks

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...