为什么在c++中注释多行注释不一致?



所以我们知道

// This doesn't affect anything
/*
This doesn't affect anything either
*/
/*
/* /* /*
This doesn't affect anything
*/
This does because comments aren't recursive
/* /*
This doesn't affect anything
*/ */
This throws an error because the second * / is unmatched since comments aren't recursive

我听说它们不是递归的原因是它们会减慢编译器的速度,我想这是有道理的。然而,现在当我在高级语言(比如Python)中解析c++代码时,我可以简单地使用正则表达式

"/[/]+((?![n])[sS])*r*n"

匹配// single line comments,并使用

"/*((?!*/)[sS])**/"

匹配/* multiline comments */,然后循环遍历所有单行注释,删除它们,然后循环遍历所有多行注释并删除它们。反之亦然。但这就是我被困住的地方。只做其中之一似乎是不够的,因为:

// /*
An error is thrown because the /* is ignored
*/
/*
This doesn't affect things because of mysterious reasons
// */

/*
This throws an error because the second * / is unmatched
// */ */

这种行为的原因是什么?它也是编译器解析事物的方式的产物吗?明确地说,我不想改变c++的行为,我只是想知道第二组示例背后的原因。

编辑:

所以,是的,更明确地说,我的问题是为什么以下三种(看似合理的)解释这种行为的方法不起作用:

  1. 直接忽略//之后的所有字符,不管它们是/*还是*/,即使你是在多行注释中。

  2. 允许/*或*/后面跟着//仍然有效。

我理解为什么不允许嵌套注释,因为它们需要堆栈和任意大量的内存。但这三种情况不会。

编辑:

如果有人感兴趣,下面的代码可以按照这里讨论的正确注释规则在python中提取c/c++文件的注释:

import re
commentScanner = re.Scanner([
  (r"/[/]+((?![n])[sS])*r*(n{1})?", lambda scanner, token: ("//", token)),
  (r"/*((?!*/)[sS])**/", lambda scanner, token: ("/* ... */", token)),
  (r"[sS]", lambda scanner, token: None)
])
commentScanner.scan("fds a45fsa//kjl fds4325lkjfa/*jfds/nklj/*4532jlfds5342a  l/*a/*b/*cn//fdsafanrn/*jfd//a*/fd// fs54fdsa3rr//r/*rn2annnois")

这不是不一致。现有的行为既容易指定也容易实现,并且你的编译器正在正确地实现它。参见标准中的[lex.comment]。

/*开始注释,以*/结束注释。这些评论不是巢。字符//开始一个注释,它以下一个换行字符结束。如果有在这样的注释中,如果出现换行符或垂直制表符,则其之间只能出现空白字符结束注释的新行;不需要诊断。[注意:注释字符///**///注释中没有特殊的含义,它们与其他字符一样被处理。类似地,注释字符///*/*注释中没有特殊含义。

可以看到,//可以用来注释掉/**/。只是注释不嵌套,所以如果//已经在/*中,那么//根本没有影响。

就像注释中的所有内容都是文本一样,但是当你删除注释分隔符时,
暴露的文本可以再次被解析。
因此,如果该文本的一部分具有注释分隔符字面量,则它们可以作为新的注释分隔符进行解析。

而且总是先到先得的问题,即从左到右的顺序。

认为解析注释很简单可能有点过于简单。
事实上,必须同时解析引号(单引号/双引号),并且首先处理第一次遇到的注释/引号。

最后,跳过注释内的所有内容意味着如果您删除外部
注释层,除有效注释之外的所有内容都将被解析为
语言的一部分。这意味着任何公开的注释格式都不确定,
如果不是不可避免的话,获得解析错误的可能性也很大。

我相信c++也有//样式注释的行延续形式。
例如:

// single line continuation
continuation                 
end here 
code

用正则表达式解析c++注释的公式是
解析(匹配)文件中的每个字符。
如果你直接打开注释它会将匹配注入
找错地方了。

下面是解析注释的一个很好的正则表达式。我最初是从一个Perl组得到的
并稍微修改了单行注释和延续。
有了它,你可以删除评论或只是找到评论。

原始正则表达式:

   # (/*[^*]**+(?:[^/*][^*]**+)*/|//(?:[^\]|\n?)*?n)|("(?:\[Ss]|[^"\])*"|'(?:\[Ss]|[^'\])*'|[Ss][^/"'\]*)

   (                                # (1 start), Comments 
        /*                              # Start /* .. */ comment
        [^*]* *+
        (?: [^/*] [^*]* *+ )*
        /                                # End /* .. */ comment
     |  
        //                               # Start // comment
        (?: [^\] | \ n? )*?           # Possible line-continuation
        n                               # End // comment
   )                                # (1 end)
|  
   (                                # (2 start), Non - comments 
        "
        (?: \ [Ss] | [^"\] )*        # Double quoted text
        "
     |  '
        (?: \ [Ss] | [^'\] )*        # Single quoted text
        ' 
     |  [Ss]                           # Any other char
        [^/"'\]*                        # Chars which doesn't start a comment, string, escape,
                                         # or line continuation (escape + newline)
   )                                # (2 end)

增强(保留格式),主要用于删除注释。使用多行模式:

   # ((?:(?:^[ t]*)?(?:/*[^*]**+(?:[^/*][^*]**+)*/(?:[ t]*r?n(?=[ t]*(?:r?n|/*|//)))?|//(?:[^\]|\(?:r?n)?)*?(?:r?n(?=[ t]*(?:r?n|/*|//))|(?=r?n))))+)|("(?:\[Ss]|[^"\])*"|'(?:\[Ss]|[^'\])*'|(?:r?n|[Ss])[^/"'\s]*)
   (                                # (1 start), Comments 
        (?:
             (?: ^ [ t]* )?                  # <- To preserve formatting
             (?:
                  /*                              # Start /* .. */ comment
                  [^*]* *+
                  (?: [^/*] [^*]* *+ )*
                  /                                # End /* .. */ comment
                  (?:                              # <- To preserve formatting 
                       [ t]* r? n                                      
                       (?=
                            [ t]*                  
                            (?: r? n | /* | // )
                       )
                  )?
               |  
                  //                               # Start // comment
                  (?:                              # Possible line-continuation
                       [^\] 
                    |  \ 
                       (?: r? n )?
                  )*?
                  (?:                              # End // comment
                       r? n                               
                       (?=                              # <- To preserve formatting
                            [ t]*                          
                            (?: r? n | /* | // )
                       )
                    |  (?= r? n )
                  )
             )
        )+                               # Grab multiple comment blocks if need be
   )                                # (1 end)
|                                 ## OR
   (                                # (2 start), Non - comments 
        "
        (?: \ [Ss] | [^"\] )*        # Double quoted text
        "
     |  '
        (?: \ [Ss] | [^'\] )*        # Single quoted text
        ' 
     |  (?: r? n | [Ss] )            # Linebreak or Any other char
        [^/"'\s]*                      # Chars which doesn't start a comment, string, escape,
                                         # or line continuation (escape + newline)
   )                                # (2 end)

当注释开始时,直到注释结束的所有内容都被视为注释。

因此,zero // one */ two本身可以将zero // one */作为前一行/* */注释的结尾,两个two在注释之外,或者它可以是一个新的单行注释,从// one */ two开始,zero在注释之外。

作为这样做的理论原因,//不是一个有效的C标记或标记序列。所以没有程序在c语言的注释或字符串之外使用//

但是,注释中的//是合法的。所以头文件包含:

/* this is a C style comment
// with some cool
// slashes */
如果我们将//注释掉后面的*/

将会断开。在/* */注释中,//被忽略。不能无缘无故破坏与C语言的兼容性。

//中,所有内容都被忽略,直到行尾。没有偷偷摸摸的/*或允许。

解析规则非常简单——开始注释,发出声音并丢弃,直到看到结束令牌(换行符或*/取决于),然后继续解析。

由于c++不是为正则表达式解析而设计的,因此使用正则表达式解析它的困难要么没有被考虑,要么被认为不重要。

最新更新