【问题标题】:Regular expression to remove comments from SQL statement从 SQL 语句中删除注释的正则表达式
【发布时间】:2012-03-30 05:34:53
【问题描述】:

我正在尝试提出一个正则表达式来从 SQL 语句中删除 cmets。

这个正则表达式几乎可以工作:

(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|'(?:[^']|'')*'|(--.*)

除了最后一部分不能很好地处理“--”cmets。问题是处理 SQL 字符串,用 '' 分隔。

例如,如果我有

SELECT ' -- Hello -- ' FROM DUAL

它不应该匹配,但它是匹配的。

这是在 ASP/VBscript 中。

我考虑过从右到左匹配,但我认为 VBScript 的正则表达式引擎不支持它。还尝试摆弄负面的后视,但结果并不好。

【问题讨论】:

    标签: sql regex vbscript


    【解决方案1】:

    在 PHP 中,我使用此代码取消注释 SQL:

    $sqlComments = '@(([\'"]).*?[^\\\]\2)|((?:\#|--).*?$|/\*(?:[^/*]|/(?!\*)|\*(?!/)|(?R))*\*\/)\s*|(?<=;)\s+@ms';
    /* Commented version
    $sqlComments = '@
        (([\'"]).*?[^\\\]\2) # $1 : Skip single & double quoted expressions
        |(                   # $3 : Match comments
            (?:\#|--).*?$    # - Single line comments
            |                # - 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';
    */
    $uncommentedSQL = trim( preg_replace( $sqlComments, '$1', $sql ) );
    preg_match_all( $sqlComments, $sql, $comments );
    $extractedComments = array_filter( $comments[ 3 ] );
    var_dump( $uncommentedSQL, $extractedComments );
    

    【讨论】:

    • 这很棒,但我不喜欢最后的修剪,因为它可以删除实际上可能需要/必要的换行符(就像代码之后的内联注释之前没有空格一样.. . 人们这样做 :| )。还在报价单中添加了反引号。所以我使用: $sqlComments = '@(([\'"`]).*?[^\\]\2)|((?:\#|--).*?$|/*( ?:[^/*]|/(?!*)|*(?!/)|(?R))**\/)+@ms';
    • 此正则表达式段错误 (php 5.6) 或在开头带有长 cmets 的查询返回 NULL (php 7+),例如 ` /* 将 8kb 的虚拟文本放在这里 */ SELECT 1; `
    • 我通过这个正则表达式运行了大约 120k 个查询,它在检测查询中间的 cmets 方面存在一些重大缺陷。例如,包含“--”(双破折号字符串)的正确封装的字符串被删除。
    • regex101.com 上测试它说:Your pattern contains one or more errors: * Character range is out of order * Unmatched parenthesis
    • @rapt,这是因为 '[^\\]' 中的 php 转义 3x``... 如果你想在 php 之外使用正则表达式:(([\'"])。 *?[^\]\2)|((?:\#|--).*?$|/*(?:[^/*]|/(?!*)|*(?!/)| (?R))**\/)\s*|(?
    【解决方案2】:

    此代码适用于我:

    function strip_sqlcomment ($string = '') {
        $RXSQLComments = '@(--[^\r\n]*)|(\#[^\r\n]*)|(/\*[\w\W]*?(?=\*/)\*/)@ms';
        return (($string == '') ?  '' : preg_replace( $RXSQLComments, '', $string ));
    }
    

    只要稍微调整一下正则表达式,它就可以用来剥离任何语言的 cmets

    【讨论】:

    • 这将删除引号内的任何内容。
    【解决方案3】:

    正如您所说,您的正则表达式的其余部分都很好,我专注于最后一部分。您需要做的就是验证-- 是否位于开头,然后确保删除所有破折号(如果超过 2 个)。结束正则表达式如下

    (^[--]+)
    

    以上只是如果您想删除评论破折号而不是整行。如果您确实希望它之后的所有内容到行尾,您可以运行以下命令,也

    (^--.*)
    

    【讨论】:

    • 嗨贾斯汀...感谢您的帮助。内联 cmets 仍然存在不从一开始就开始的问题。 Like SELECT ' -- Hello -- ' FROM DUAL -- 应该删除的注释
    • 没问题,欢迎栈溢出。请记住,这里表示感谢的方式是通过投票和接受的答案(答案旁边的复选标记)。更多信息可以在FAQ中找到,尤其是FAQ如何提问
    【解决方案4】:

    最初,我使用@Adrien Gibrat 的解决方案。但是,我遇到了一种情况,它没有正确解析带引号的字符串,如果我在其中有任何带有前面的“--”的话。我最终写了这个,而不是:

    '[^']*(?!\\)'(*SKIP)(*F)       # Make sure we're not matching inside of quotes
    |(?m-s:\s*(?:\-{2}|\#)[^\n]*$) # Single line comment
    |(?:
      \/\*.*?\*\/                  # Multi-line comment
      (?(?=(?m-s:\h+$))         # Get trailing whitespace if any exists and only if it's the rest of the line
        \h+
      )
    )
    
    # Modifiers used: 'xs' ('g' can be used as well, but is enabled by default in PHP)
    

    请注意,这应该在 PCRE 可用时使用。所以,就我而言,我在我的 PHP 库中使用了它的一个变体。

    Example

    【讨论】:

    • 这太棒了!我已经更新了正则表达式,因此它不仅忽略了单引号中的 cmets,还忽略了双引号和反引号 - regex101.com/r/GXb0a5/2
    • 迄今为止只有一个可靠工作。还没有找到这个的任何边缘案例。
    • @OndřejHlaváček 评论代码得到改进并且运行良好。
    【解决方案5】:

    删除 /**/ 和 -- cmets

    function unComment($sql){
    
            $re = '/(--[^\n]*)/i';
            $sql = preg_replace( $re, '', $sql );
    
            $sqlComments = '@(([\'"]).*?[^\\\]\2)|((?:\#|--).*?$|/\*(?:[^/*]|/(?!\*)|\*(?!/)|(?R))*\*\/)\s*|(?<=;)\s+@ms';
            $uncommentedSQL = trim( preg_replace( $sqlComments, '$1', $sql ) );
            preg_match_all( $sqlComments, $sql, $comments );
            $sql = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', trim($uncommentedSQL));
    
    
            return $sql;
        }
    

    【讨论】:

      【解决方案6】:

      请看我的回答here。它既适用于line cmets,也适用于block cmets,甚至适用于nested block cmets。我想您需要将正则表达式与 平衡组 一起使用,而 AFAIK 在 VBScript 中不可用。

      【讨论】:

        【解决方案7】:

        对于 Node.js,请参阅 pg-minify 库。它适用于 PostgreSQL、MS-SQL 和 MySQL 脚本。

        它可以处理所有类型的 cmets,并将生成的 SQL 压缩到最低限度,以优化需要发送到服务器的内容。

        【讨论】:

          【解决方案8】:

          对于所有 PHP 人:请使用这个库 - https://github.com/jdorn/sql-formatter。几年来,我一直在处理从 SQL 中剥离 cmets,唯一有效的解决方案是分词器/状态机,我懒惰地拒绝编写它。几天前,我发现了这个库,并通过它运行了 120k 查询,只发现了一个错误 (https://github.com/jdorn/sql-formatter/issues/93),它立即在我们的 fork https://github.com/keboola/sql-formatter 中修复。

          用法很简单

          $query <<<EOF
          /* 
            my comments 
          */
          SELECT 1;
          EOF;
          
          $bareQuery = \SqlFormatter::removeComments($query);
          // prints "SELECT 1;"
          print $bareQuery;
          

          【讨论】:

          • @BaummitAugen 谢谢,修正了答案。
          猜你喜欢
          • 2012-04-08
          • 1970-01-01
          • 2011-04-01
          • 1970-01-01
          • 2017-08-06
          • 2011-01-28
          • 2016-07-07
          • 2022-08-19
          相关资源
          最近更新 更多