【问题标题】:Java: Removing comments from stringJava:从字符串中删除注释
【发布时间】:2012-01-27 10:50:24
【问题描述】:

我想做一个获取字符串的函数,如果它有内联 cmets,它会删除它。我知道这听起来很简单,但我想确保我做对了,例如:

private String filterString(String code) {
  // lets say code = "some code //comment inside"

  // return the string "some code" (without the comment)
}

我想到了 2 种方法:如果有其他方法,请随时提出建议

  1. 迭代字符串并查找双内联括号并使用子字符串方法。
  2. 正则表达式方式..(我不太确定)

你能告诉我什么是最好的方法并告诉我应该怎么做吗? (请不要建议太高级的解决方案)

已编辑:这可以通过 Scanner 对象以某种方式完成吗? (无论如何我都在使用这个对象)

【问题讨论】:

    标签: java string comments


    【解决方案1】:

    对于扫描仪,使用分隔符,

    分隔符示例。

    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.util.Scanner;
    
    public class MainClass {
      public static void main(String args[]) throws IOException {
    FileWriter fout = new FileWriter("test.txt");
    fout.write("2, 3.4,    5,6, 7.4, 9.1, 10.5, done");
    fout.close();
    
    FileReader fin = new FileReader("Test.txt");
    Scanner src = new Scanner(fin);
    // Set delimiters to space and comma.
    // ", *" tells Scanner to match a comma and zero or more spaces as
    // delimiters.
    
    src.useDelimiter(", *");
    
    // Read and sum numbers.
    while (src.hasNext()) {
      if (src.hasNextDouble()) {
        System.out.println(src.nextDouble());
      } else {
        break;
      }
    }
    fin.close();
      }
    }
    

    对普通字符串使用分词器

    分词器:

    // start with a String of space-separated words
    String tags = "pizza pepperoni food cheese";
    
    // convert each tag to a token
    StringTokenizer st = new StringTokenizer(tags," ");
    
    while ( st.hasMoreTokens() )
    {
      String token = (String)st.nextToken();
      System.out.println(token);
    }
    
    http://www.devdaily.com/blog/post/java/java-faq-stringtokenizer-example
    

    【讨论】:

    • 谢谢,但我不明白它与我的问题有什么关系,在你的例子中你没有考虑我给出的字符串作为例子。另外我很抱歉,但我尽量不使用太高级的解决方案
    • 我看到你刚刚在你的建议中添加了另一部分,谢谢,但这仍然不能解决我的问题,我想做一个干净的函数,我看不出它有什么帮助。
    【解决方案2】:

    使用正则表达式替换来查找常量子字符串之前的子字符串有点多。

    您可以使用indexOf() 来检查评论开始的位置,并使用substring() 来获取第一部分,例如:

    String code = "some code // comment";
    int    offset = code.indexOf("//");
    
    if (-1 != offset) {
        code = code.substring(0, offset);
    }
    

    【讨论】:

    • 这不适用于您自己的代码,它会删除字符串中的“//注释”。
    • 我不需要处理 /** cmets :) 我检查了这个解决方案,它工作正常!
    • 太简单了——会弄乱类似的东西:String url="http://www.google.com";
    • 我正在寻找一种方法来删除字符串中的所有注释行。对于 /* */ 和 // 样式 cmets 检查这个答案,它帮助了我:stackoverflow.com/a/2613945/1005102
    • 这将破坏包含字符串文字中的注释开始字符序列的源代码。
    【解决方案3】:

    只需使用 String 类中的 replaceAll 方法,并结合一个简单的正则表达式。操作方法如下:

    import java.util.*;
    import java.lang.*;
    
    class Main
    {
            public static void main (String[] args) throws java.lang.Exception
            {
                    String s = "private String filterString(String code) {\n" +
    "  // lets say code = \"some code //comment inside\"\n" +
    "  // return the string \"some code\" (without the comment)\n}";
    
                    s = s.replaceAll("//.*?\n","\n");
                    System.out.println("s=" + s);
    
            }
    }
    

    关键是行:

    s = s.replaceAll("//.*?\n","\n");
    

    正则表达式 //.*?\n 匹配以 // 开头直到行尾的字符串。

    如果您想查看此代码的运行情况,请访问此处:http://www.ideone.com/e26Ve

    希望对你有帮助!

    【讨论】:

    • 你能解释一下这个正则表达式吗?我只需要删除“//一些文本”,看起来它会影响更多的字符,例如“\n”..确切的正则表达式应该是什么?
    • 该行应为 s = s.replaceAll("//.*?\n","\n");我会编辑帖子并更正它。您“选择”的解决方案在多行字符串上无法正常工作,就像您给出的示例一样。
    • 正则表达式解决方案和您提供的解决方案将破坏包含字符串文字中的注释开始字符序列的源代码。
    【解决方案4】:

    最好的方法是使用正则表达式。 首先找到/**/ cmets 然后删除所有// commnets。例如:

    private String filterString(String code) {
      String partialFiltered = code.replaceAll("/\\*.*\\*/", "");
      String fullFiltered = partialFiltered.replaceAll("//.*(?=\\n)", "")
    }
    

    【讨论】:

    • 这会破坏包含字符串文字中的注释开始字符序列的源代码。
    【解决方案5】:

    如果您想要一个更高效的正则表达式来真正匹配所有类型的 cmets,请使用这个:

    replaceAll("(?:/\\*(?:[^*]|(?:\\*+[^*/]))*\\*+/)|(?://.*)","");
    

    来源:http://ostermiller.org/findcomment.html

    编辑:

    如果您不确定是否使用正则表达式,另一种解决方案是设计一个小型自动机,如下所示:

    public static String removeComments(String code){
        final int outsideComment=0;
        final int insideLineComment=1;
        final int insideblockComment=2;
        final int insideblockComment_noNewLineYet=3; // we want to have at least one new line in the result if the block is not inline.
        
        int currentState=outsideComment;
        String endResult="";
        Scanner s= new Scanner(code);
        s.useDelimiter("");
        while(s.hasNext()){
            String c=s.next();
            switch(currentState){
                case outsideComment: 
                    if(c.equals("/") && s.hasNext()){
                        String c2=s.next();
                        if(c2.equals("/"))
                            currentState=insideLineComment;
                        else if(c2.equals("*")){
                            currentState=insideblockComment_noNewLineYet;
                        }
                        else 
                            endResult+=c+c2;
                    }
                    else
                        endResult+=c;
                    break;
                case insideLineComment:
                    if(c.equals("\n")){
                        currentState=outsideComment;
                        endResult+="\n";
                    }
                break;
                case insideblockComment_noNewLineYet:
                    if(c.equals("\n")){
                        endResult+="\n";
                        currentState=insideblockComment;
                    }
                case insideblockComment:
                    while(c.equals("*") && s.hasNext()){
                        String c2=s.next();
                        if(c2.equals("/")){
                            currentState=outsideComment;
                            break;
                        }
                        
                    }
                    
            }
        }
        s.close();
        return endResult;   
    }
    

    【讨论】:

    • 正则表达式解决方案和您提供的解决方案将破坏包含字符串文字中的注释开始字符序列的源代码。
    • 真的,感谢您的注意,我没有过多关注这些案例,因为在我遇到这个问题时它们与我无关(并发布了这个答案)不过,字符串声明中的 cmets 应该不难实现,尤其是对于第二种解决方案。
    【解决方案6】:

    为此我做了一个开源的library (on GitHub),它叫做CommentRemover,你可以删除单行和多行Java评论。

    它支持删除或不删除 TODO。
    它还支持 JavaScript 、 HTML 、 CSS 、 Properties 、 JSP 和 XML Comments。

    小代码sn-p怎么用(有2种用法):

    第一路内部路径

     public static void main(String[] args) throws CommentRemoverException {
    
     // root dir is: /Users/user/Projects/MyProject
     // example for startInternalPath
    
     CommentRemover commentRemover = new CommentRemover.CommentRemoverBuilder()
            .removeJava(true) // Remove Java file Comments....
            .removeJavaScript(true) // Remove JavaScript file Comments....
            .removeJSP(true) // etc.. goes like that
            .removeTodos(false) //  Do Not Touch Todos (leave them alone)
            .removeSingleLines(true) // Remove single line type comments
            .removeMultiLines(true) // Remove multiple type comments
            .startInternalPath("src.main.app") // Starts from {rootDir}/src/main/app , leave it empty string when you want to start from root dir
            .setExcludePackages(new String[]{"src.main.java.app.pattern"}) // Refers to {rootDir}/src/main/java/app/pattern and skips this directory
            .build();
    
     CommentProcessor commentProcessor = new CommentProcessor(commentRemover);
                      commentProcessor.start();        
      }
    

    第二种方式外部路径

     public static void main(String[] args) throws CommentRemoverException {
    
     // example for externalPath
    
     CommentRemover commentRemover = new CommentRemover.CommentRemoverBuilder()
            .removeJava(true) // Remove Java file Comments....
            .removeJavaScript(true) // Remove JavaScript file Comments....
            .removeJSP(true) // etc..
            .removeTodos(true) // Remove todos
            .removeSingleLines(false) // Do not remove single line type comments
            .removeMultiLines(true) // Remove multiple type comments
            .startExternalPath("/Users/user/Projects/MyOtherProject")// Give it full path for external directories
            .setExcludePackages(new String[]{"src.main.java.model"}) // Refers to /Users/user/Projects/MyOtherProject/src/main/java/model and skips this directory.
            .build();
    
     CommentProcessor commentProcessor = new CommentProcessor(commentRemover);
                      commentProcessor.start();        
      }
    

    【讨论】:

    • 我如何得到结果?不返回也不写回源文件...
    • @BullyWiiPlaza 如果没有这样的功能,您想获得他们的 cmets 删除的类的列表。但如果出现问题,库会显示无法删除的类列表。
    • 这很好用。如果您只是想为外部路径运行它,您甚至不需要添加“setExcludePackages”设置器。我克隆了它,并且在删除“setExcludePackages”设置器后能够运行外部路径示例,没有任何问题。
    【解决方案7】:

    @Christian Hujer 已正确指出,如果 cmets 出现在字符串中,则发布的许多或所有解决方案都会失败。

    @Loïc Gammaitoni 建议他的自动机方法可以很容易地扩展到处理这种情况。这是那个扩展名。

    enum State { outsideComment, insideLineComment, insideblockComment, insideblockComment_noNewLineYet, insideString };
    
    public static String removeComments(String code) {
      State state = State.outsideComment;
      StringBuilder result = new StringBuilder();
      Scanner s = new Scanner(code);
      s.useDelimiter("");
      while (s.hasNext()) {
        String c = s.next();
        switch (state) {
          case outsideComment:
            if (c.equals("/") && s.hasNext()) {
              String c2 = s.next();
              if (c2.equals("/"))
                state = State.insideLineComment;
              else if (c2.equals("*")) {
                state = State.insideblockComment_noNewLineYet;
              } else {
                result.append(c).append(c2);
              }
            } else {
              result.append(c);
              if (c.equals("\"")) {
                state = State.insideString;
              }
            }
            break;
          case insideString:
            result.append(c);
            if (c.equals("\"")) {
              state = State.outsideComment;
            } else if (c.equals("\\") && s.hasNext()) {
              result.append(s.next());
            }
            break;
          case insideLineComment:
            if (c.equals("\n")) {
              state = State.outsideComment;
              result.append("\n");
            }
            break;
          case insideblockComment_noNewLineYet:
            if (c.equals("\n")) {
              result.append("\n");
              state = State.insideblockComment;
            }
          case insideblockComment:
            while (c.equals("*") && s.hasNext()) {
              String c2 = s.next();
              if (c2.equals("/")) {
                state = State.outsideComment;
                break;
              }
            }
        }
      }
      s.close();
      return result.toString();
    }
    

    【讨论】:

      【解决方案8】:

      如果代码单独处理单行注释和多行注释会更好。有什么建议吗?

          public class RemovingCommentsFromFile {
      
      public static void main(String[] args) throws IOException {
      
          BufferedReader fin = new BufferedReader(new FileReader("/home/pathtofilewithcomments/File"));
          BufferedWriter fout = new BufferedWriter(new FileWriter("/home/result/File1"));
      
      
          boolean multilinecomment = false;
          boolean singlelinecomment = false;
      
      
          int len,j;
          String s = null;
          while ((s = fin.readLine()) != null) {
      
              StringBuilder obj = new StringBuilder(s);
      
              len = obj.length();
      
              for (int i = 0; i < len; i++) {
                  for (j = i; j < len; j++) {
                      if (obj.charAt(j) == '/' && obj.charAt(j + 1) == '*') {
                          j += 2;
                          multilinecomment = true;
                          continue;
                      } else if (obj.charAt(j) == '/' && obj.charAt(j + 1) == '/') {
                          singlelinecomment = true;
                          j = len;
                          break;
                      } else if (obj.charAt(j) == '*' && obj.charAt(j + 1) == '/') {
                          j += 2;
                          multilinecomment = false;
                          break;
                      } else if (multilinecomment == true)
                          continue;
                      else
                          break;
                  }
                  if (j == len)
                  {
                      singlelinecomment=false;
                      break;
                  }
                  else
                      i = j;
      
                  System.out.print((char)obj.charAt(i));
                  fout.write((char)obj.charAt(i));
              }
              System.out.println();
              fout.write((char)10);
          }
          fin.close();
          fout.close();
      
      }
      

      【讨论】:

        猜你喜欢
        • 2017-01-19
        • 2014-04-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-05
        • 2016-08-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多