如何找到源代码中的所有注释?
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。看看最后一段(“警告”),看看它何时失败。 谢谢,我没有仔细阅读注意事项。更正了我的答案。以上是关于如何找到源代码中的所有注释?的主要内容,如果未能解决你的问题,请参考以下文章
JSF 如何找到用@ManagedBean 注释的bean?