如何找到源代码中的所有注释?

Posted

技术标签:

【中文标题】如何找到源代码中的所有注释?【英文标题】:How to find all comments in the source code? 【发布时间】:2011-10-02 04:12:50 【问题描述】:

cmets有两种风格,C风格和C++风格,如何识别?

/* comments */

// comments

我可以随意使用任何方法和第三库。

【问题讨论】:

你能再具体一点吗?找到 cmets 后,您想对它们做什么? “认识”他们是什么意思? 以下是.java 源文件中的有效注释:\u002f\u002a multi line comment \u002A/。请注意\u002f == '/'\u002a == '*'。你也要考虑这些吗? 仅在 java 中有效的 cmets @user705414,我给出的示例在Java中的有效注释。 @user705414,您的评论:“我并不真正关心 cmets 的 unicode 形式”“java 中只有有效的 cmets” 是矛盾的。如果您不关心极端情况,那么 "only valid cmets inside java" 不正确。 【参考方案1】:

为了可靠地找到 Java 源文件中的所有 cmets,我不会使用正则表达式,而是使用真正的词法分析器(又名分词器)。

Java 的两个流行选择是:

JFlex:http://jflex.de ANTLR:http://www.antlr.org

与流行的看法相反,ANTLR 也可用于创建没有解析器的词法分析器。

这是一个快速的 ANTLR 演示。您需要在同一目录中的以下文件:

antlr-3.2.jar JavaCommentLexer.g(语法) Main.java Test.java(一个有效的 (!) java 源文件,带有奇异的 cmets)

JavaCommentLexer.g

lexer grammar JavaCommentLexer;

options 
  filter=true;


SingleLineComment
  :  FSlash FSlash ~('\r' | '\n')*
  ;

MultiLineComment
  :  FSlash Star .* Star FSlash
  ;

StringLiteral
  :  DQuote
     ( (EscapedDQuote)=> EscapedDQuote
     | (EscapedBSlash)=> EscapedBSlash
     | Octal
     | Unicode
     | ~('\\' | '"' | '\r' | '\n')
     )*
     DQuote skip();
  ;

CharLiteral
  :  SQuote
     ( (EscapedSQuote)=> EscapedSQuote
     | (EscapedBSlash)=> EscapedBSlash
     | Octal
     | Unicode
     | ~('\\' | '\'' | '\r' | '\n')
     )
     SQuote skip();
  ;

fragment EscapedDQuote
  :  BSlash DQuote
  ;

fragment EscapedSQuote
  :  BSlash SQuote
  ;

fragment EscapedBSlash
  :  BSlash BSlash
  ;

fragment FSlash
  :  '/' | '\\' ('u002f' | 'u002F')
  ;

fragment Star
  :  '*' | '\\' ('u002a' | 'u002A')
  ;

fragment BSlash
  :  '\\' ('u005c' | 'u005C')?
  ;

fragment DQuote
  :  '"' 
  |  '\\u0022'
  ;

fragment SQuote
  :  '\'' 
  |  '\\u0027'
  ;

fragment Unicode
  :  '\\u' Hex Hex Hex Hex
  ;

fragment Octal
  :  '\\' ('0'..'3' Oct Oct | Oct Oct | Oct)
  ;

fragment Hex
  :  '0'..'9' | 'a'..'f' | 'A'..'F'
  ;

fragment Oct
  :  '0'..'7'
  ;

Main.java

import org.antlr.runtime.*;

public class Main 
  public static void main(String[] args) throws Exception 
    JavaCommentLexer lexer = new JavaCommentLexer(new ANTLRFileStream("Test.java"));
    CommonTokenStream tokens = new CommonTokenStream(lexer);
      for(Object o : tokens.getTokens()) 
      CommonToken t = (CommonToken)o;
      if(t.getType() == JavaCommentLexer.SingleLineComment) 
        System.out.println("SingleLineComment :: " + t.getText().replace("\n", "\\n"));
      
      if(t.getType() == JavaCommentLexer.MultiLineComment) 
        System.out.println("MultiLineComment  :: " + t.getText().replace("\n", "\\n"));
      
    
  

Test.java

\u002f\u002a <- multi line comment start
multi
line
comment // not a single line comment
\u002A/
public class Test 

  // single line "not a string"

  String s = "\u005C" \242 not // a comment \\\" \u002f \u005C\u005C \u0022;
  /*
  regular multi line comment
  */
  char c = \u0027"'; // the " is not the start of a string

  char q1 = '\u005c'';                  // == '\''
  char q2 = '\u005c\u0027';             // == '\''
  char q3 = \u0027\u005c\u0027\u0027;   // == '\''
  char c4 = '\047';

  String t = "/*";
  \u002f\u002f another single line comment
  String u = "*/";

现在,要运行演示,请执行以下操作:

bart@hades:~/Programming/ANTLR/Demos/JavaComment$ java -cp antlr-3.2.jar org.antlr.Tool JavaCommentLexer.g
bart@hades:~/Programming/ANTLR/Demos/JavaComment$ javac -cp antlr-3.2.jar *.java
bart@hades:~/Programming/ANTLR/Demos/JavaComment$ java -cp .:antlr-3.2.jar Main

您会看到控制台打印出以下内容:

MultiLineComment  :: \u002f\u002a <- multi line comment start\nmulti\nline\ncomment // not a single line comment\n\u002A/
SingleLineComment :: // single line "not a string"
SingleLineComment :: // a comment \\\" \u002f \u005C\u005C \u0022;
MultiLineComment  :: /*\n  regular multi line comment\n  */
SingleLineComment :: // the " is not the start of a string
SingleLineComment :: // == '\''
SingleLineComment :: // == '\''
SingleLineComment :: // == '\''
SingleLineComment :: \u002f\u002f another single line comment

编辑

当然,您可以自己使用正则表达式创建一种词法分析器。但是,以下演示不处理源文件中的 Unicode 文字:

Test2.java

/* <- multi line comment start
multi
line
comment // not a single line comment
*/
public class Test2 

  // single line "not a string"

  String s = "\" \242 not // a comment \\\" ";
  /*
  regular multi line comment
  */
  char c = '"'; // the " is not the start of a string

  char q1 = '\'';                  // == '\''
  char c4 = '\047';

  String t = "/*";
  // another single line comment
  String u = "*/";

Main2.java

import java.util.*;
import java.io.*;
import java.util.regex.*;

public class Main2 

  private static String read(File file) throws IOException 
    StringBuilder b = new StringBuilder();
    Scanner scan = new Scanner(file);
    while(scan.hasNextLine()) 
      String line = scan.nextLine();
      b.append(line).append('\n');
    
    return b.toString();
  

  public static void main(String[] args) throws Exception 
    String contents = read(new File("Test2.java"));

    String slComment = "//[^\r\n]*";
    String mlComment = "/\\*[\\s\\S]*?\\*/";
    String strLit = "\"(?:\\\\.|[^\\\\\"\r\n])*\"";
    String chLit = "'(?:\\\\.|[^\\\\'\r\n])+'";
    String any = "[\\s\\S]";

    Pattern p = Pattern.compile(
        String.format("(%s)|(%s)|%s|%s|%s", slComment, mlComment, strLit, chLit, any)
    );

    Matcher m = p.matcher(contents);

    while(m.find()) 
      String hit = m.group();
      if(m.group(1) != null) 
        System.out.println("SingleLine :: " + hit.replace("\n", "\\n"));
      
      if(m.group(2) != null) 
        System.out.println("MultiLine  :: " + hit.replace("\n", "\\n"));
      
    
  

如果你运行Main2,控制台会打印以下内容:

MultiLine  :: /* <- multi line comment start\nmulti\nline\ncomment // not a single line comment\n*/
SingleLine :: // single line "not a string"
MultiLine  :: /*\n  regular multi line comment\n  */
SingleLine :: // the " is not the start of a string
SingleLine :: // == '\''
SingleLine :: // another single line comment

【讨论】:

漂亮的解决方案。但是,如果我不关心字符串中的 unicode 和注释字符等边缘情况,我可以使用正则表达式吗? 你不关心异国情调的Unicode文字,我可以想象。但我不明白忽略字符串文字中的内容。如果您想忘记所有这些,当然,请继续使用正则表达式,但是您将完全忽略您的要求 "only valid cmets inside java"。一个正则表达式解决方案可能会阻塞String s = "//";String s = "/*";(至少,到目前为止发布的正则表达式解决方案)。 @user705414,参见编辑 ANTLR.org 的 URL 有错字。应该是antlr.org。我尝试编辑,但由于不满足编辑所需的最少字符数而被阻止。【参考方案2】:

编辑:我已经搜索了一段时间,但这是真正的工作正则表达式:

String regex = "((//[^\n\r]*)|(/\\*(.+?)\\*/))"; // New Regex
List<String> comments = new ArrayList<String>();

Pattern p = Pattern.compile(regex, Pattern.DOTALL);
Matcher m = p.matcher(code);
// code is the C-Style code, in which you want to serach
while (m.find())

    System.out.println(m.group(1));
    comments.add(m.group(1));

有了这个输入:

import Blah;
//Comment one//
line();
/* Blah */
line2(); // something weird
/* Multiline
another line for the comment
*/

它生成这个输出:

//Comment one//
/* Blah */
line2(); // something weird
/* Multiline
another line for the comment
*/

请注意,输出的最后三行是一次打印。

【讨论】:

这失败了,正如 Tomasz 的建议。在源代码中添加String s = "foo /* bar";(字符串文字)甚至\u002f\u002a multi line comment \u002A/有效注释)。 另外,Pattern.MULTILINE 不是必需的,您可能希望将\r 包含在[^\n] 类中(尽管大多数换行符都以\n 结尾...但仍然很旧Mac 文件可能仅以 \r 结束其行。 @Martjin,很好的解决方案。但是, line2();本身不是评论。怎么去掉? 呵呵,回想起来很有趣:正则表达式并不适用于非正则语言。除非我当时知道更多的理论?【参考方案3】:

你试过正则表达式吗? Here 是对 Java 示例的一个很好的总结。 可能需要一些调整 但是,对于更复杂的结构(嵌套 cmets、字符串中的“cmets”)仅使用正则表达式是不够的,但这是一个不错的开始。

【讨论】:

“一些”调整有点轻描淡写,IMO。看看最后一段(“警告”),看看它何时失败。 谢谢,我没有仔细阅读注意事项。更正了我的答案。

以上是关于如何找到源代码中的所有注释?的主要内容,如果未能解决你的问题,请参考以下文章

如何找到具有特定注释的所有 Java 对象(不是类)?

JSF 如何找到用@ManagedBean 注释的bean?

真正的开发中,java代码要如何注释?

如何在 Xcode 中添加一些注释 [重复]

使用注释拉出所有字段(包括另一个对象内的字段和内部类中的字段)

如何获取java代码中的注释