仅从For Loops块中删除System.out语句

时间:2013-01-24 18:52:38

标签: java python regex perl code-editor

任何方法都可以轻松地从文件中的for循环块中删除它们 ...

之前

for( ... ) {
...
System.out.println("string");
...
System.out.println("string");
...
}

之后

for( ... ) {
...
... 
...
}

4 个答案:

答案 0 :(得分:6)

这很棘手:哪个右括号关闭了for - 循环?要么解析整个代码,要么使用一些启发式。在下面的解决方案中,我要求右括号的意图与for关键字的意图相同:

$ perl -nE'
    if( /^(\s*)for\b/ .. /^$ws\}/ ) {
      $ws = $1 // $ws;
      /^\s*System\.out\.println/ or print;
    } else { print }'

这使用触发器操作符COND1 .. COND2。该脚本可以用作简单的过滤器

$ perl -nE'...' <source >processed

或备份功能:

$ perl -i.bak -nE'...' source

(创建文件source.bak作为备份)。

仅针对示例输入进行测试;不是一个明智的测试套件。
这个脚本通过了GLES Prateek Nina测试。

要在目录中的所有Java文件上运行此脚本,请执行

$ perl -i.bak -nE'...' *.java

修改

在Windows系统上,分隔符必须更改为"。此外,我们必须自己做所有的游戏。

> perl -nE"if(/^(\s*)for\b/../^$ws\}/){$ws=$1//$ws;/^\s*System\.out\.println/ or print}else{print}BEGIN{@ARGV=$#ARGV?@ARGV:glob$ARGV[0]}" *.java

编辑2

这是我在评论中概述的括号计数算法的实现。此解决方案也可以进行备份。命令行参数将被解释为glob表达式。

#!/usr/bin/perl
use strict; use warnings;

clean($_) for map glob($_), @ARGV;

sub clean {
    local @ARGV = @_;
    local $^I = ".bak";
    my $depth = 0;
    while (<>) {
        $depth ||= /^\s*for\b/ ? "0 but true" : 0;
        my $delta = ( ()= /\{/g ) - ( ()= /\}/g );
        $depth += $delta if $depth && $delta;
        $depth = 0 if $depth < 0;
        print unless $depth && /^\s*System\.out\.println/;
    }
    return !!1;
}

这也不做评论。这只会重新识别System.out.println - 开始新行的语句。

使用示例:> perl thisScript.pl *.java

这是一个带有伪java语法的测试文件,我用它进行测试。标记为XXX的所有行都将在脚本运行后消失。

/** Java test suite **/

bare block {
    System.out.println(...); // 1 -- let stand
}

if (true) {
    for (foo in bar) {
        System.out.println; // 2 XXX
        if (x == y) {
            // plz kill this
            System.out.println // 3 XXX
        } // don't exit here
        System.out.println // 4 XXX
    }
}

for (...) {
    for {
        // will this be removed?
        System.out.println // 5 XXX
    }
}

/* pathological cases */

// intendation
for (...) { System.out.println()/* 6 */} 

// intendation 2
for (...)
{
    if (x)
    {
        System.out.println // 7 XXX
    }}

// inline weirdness
for (...) {
    // "confuse" script here
    foo = new baz() {void qux () {...}
    };
    System.out.println // 8 XXX
}

№1应该留下来,并且确实如此。声明№6应删除;但这些脚本无法这样做。

答案 1 :(得分:4)

我建议使用静态代码分析器PMD来定位问题语句和删除行的简单脚本。所有源代码和配置都包含在下面, EDIT ,包括Python和Groovy备选方案。

PMD有一个扩展机制,允许new rules to be added非常简单地使用一个简单的XPath表达式。在下面的实现中,我使用:

        //WhileStatement/Statement/descendant-or-self::
            Statement[./StatementExpression/PrimaryExpression/PrimaryPrefix/Name[@Image="System.out.println"]]
        |
        //ForStatement/Statement/descendant-or-self::
            Statement[./StatementExpression/PrimaryExpression/PrimaryPrefix/Name[@Image="System.out.println"]]

使用这种方法的好处是:

  • 没有正则表达式
  • Graphical editor制定和完善规则 - 您可以微调我为处理您拥有的任何其他方案而制定的规则
  • 处理Java源代码的所有奇怪格式 - PMD使用JavaCC编译器来理解所有有效Java文件的结构
  • 处理System.out.println在循环中的条件中的任何深度
  • 处理语句在多行中分割的位置。
  • 可在EclipseIntelliJ
  • 中使用

说明

  1. 在自定义规则集中创建PMD规则。

    • rulesets/java的某处创建目录CLASSPATH - 如果“。”,它可能位于工作目录下。正在路上。
    • 在此目录中,创建一个名为custom.xml的规则集XML文件,其中包含:

      <?xml version="1.0"?>
      <ruleset name="Custom"
          xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
      
          <description>Detecting System.out.println's</description>
          <rule name="LoopedSystemOutPrintlns"
                message="System.out.println() statements in a for or while loop"
                language="java"
                class="net.sourceforge.pmd.lang.rule.XPathRule">
            <description>
               Find System.out.println() statements in for or while loops.
            </description>
            <priority>1</priority>
            <properties>
              <property name="xpath">
              <value>
                <![CDATA[
      //WhileStatement/Statement/descendant-or-self::
          Statement[./StatementExpression/PrimaryExpression/PrimaryPrefix/Name[@Image="System.out.println"]]
      |
      //ForStatement/Statement/descendant-or-self::
          Statement[./StatementExpression/PrimaryExpression/PrimaryPrefix/Name[@Image="System.out.println"]]
                ]]>
                </value>
              </property>
            </properties>
          </rule>
      </ruleset>
      
    • 创建包含以下行的rulesets.properties文件:

      rulesets.filenames=rulesets/java/custom.xml
      
    • 大! PMD现在已经配置了新规则,可以识别代码中任何位置的任何循环内System.out.println的所有场合。您的规则集现在称为“java-custom”,因为它是“java”目录中的“custom.xml”

  2. 在您的代码库上运行PMD,只选择规则集java-custom。使用XML报告获取起始结束行。在“violation.xml”文件中捕获结果:

    $ pmd -d <SOURCEDIR> -f xml -r java-custom > violations.xml
    

    生成类似于:

    的文件
    <?xml version="1.0" encoding="UTF-8"?>
    <pmd version="5.0.1" timestamp="2013-01-28T11:22:25.688">
    <file name="SOURCEDIR/Example.java">
    <violation beginline="7" endline="11" begincolumn="13" endcolumn="39" rule="LoopedSystemOutPrintlns" ruleset="Custom" class="Example" method="bar" priority="1">
    System.out.println() statements in a for or while loop
    </violation>
    <violation beginline="15" endline="15" begincolumn="13" endcolumn="38" rule="LoopedSystemOutPrintlns" ruleset="Custom" class="Example" method="bar" priority="1">
    System.out.println() statements in a for or while loop
    </violation>
    <violation beginline="18" endline="18" begincolumn="13" endcolumn="38" rule="LoopedSystemOutPrintlns" ruleset="Custom" class="Example" method="bar" priority="1">
    System.out.println() statements in a for or while loop
    </violation>
    <violation beginline="20" endline="21" begincolumn="17" endcolumn="39" rule="LoopedSystemOutPrintlns" ruleset="Custom" class="Example" method="bar" priority="1">
    System.out.println() statements in a for or while loop
    </violation>
    </file>
    </pmd>
    

    您可以使用此报告检查PMD是否已识别出正确的陈述。

  3. 创建Python脚本(注意:答案底部给出了Groovy替代方案)以读取违规XML文件并处理源文件

    • 在类路径
    • 中的目录上创建名为remover.py的文件
    • 将以下Python添加到其中:

      from xml.etree.ElementTree import ElementTree
      from os import rename, path
      from sys import argv
      
      def clean_file(source, target, violations):
          """Read file from source outputting all lines, *except* those in the set
          violations, to the file target"""
          infile  = open(source, 'r' )
          outfile = open(target, "w")
          for num, line in enumerate(infile.readlines(), start=1):
              if num not in violations:
                  outfile.write(line)
          infile.close()
          outfile.close()
      
      
      def clean_all(pmd_xml):
          """Read a PMD violations XML file; for each file identified, remove all 
          lines that are marked as violations"""
          tree = ElementTree()
          tree.parse(pmd_xml)
          for file in tree.findall("file"):
              # Create a list of lists. Each inner list identifies all the lines
              # in a single violation.
              violations = [ range(int(violation.attrib['beginline']), int(violation.attrib['endline'])+1) for violation in file.findall("violation")]
              # Flatten the list of lists into a set of line numbers
              violations = set( i for j in violations for i in j )
      
              if violations:
                  name = file.attrib['name']
                  bak  = name + ".bak"
                  rename(name, bak)
                  clean_file(bak, name, violations)
      
      if __name__ == "__main__":
          if len(argv) != 2 or not path.exists(argv[1]):
              exit(argv[0] + " <PMD violations XML file>")
          clean_all(argv[1])
      
  4. 运行Python脚本。这将通过添加“.bak”重命名匹配的文件,然后在没有违规行的情况下重写Java文件。 这可能具有破坏性,因此请确保首先正确备份文件。特别是,不要连续两次运行脚本 - 第二轮将天真地删除相同的行号,即使它们已被删除:

    $ python remover.py violations.xml
    

  5. 修改

    对于那些喜欢更加面向Java的脚本从System.out.println中删除violations.xml语句的人,我将介绍以下Groovy:

        def clean_file(source, target, violations) {
            new File(target).withWriter { out ->
                new File(source).withReader { reader ->
                    def i = 0
                    while (true) {
                        def line = reader.readLine()
                        if (line == null) {
                            break
                        }  else {
                            i++
                            if(!(i in violations)) {
                                out.println(line)
                            }
                        }
                    }
                }
            }
        }
    
        def linesToRemove(file_element) {
            Set lines = new TreeSet()
            for (violation in file_element.children()) {
                def i = Integer.parseInt(violation.@beginline.text())
                def j = Integer.parseInt(violation.@endline.text())
                lines.addAll(i..j)
            }
            return lines
        }
    
        def clean_all(file_name) {
            def tree = new XmlSlurper().parse(file_name)
            for (file in tree.children()) {
                def violations = linesToRemove(file)
                if (violations.size() > 0) {
                    def origin = file.@name.text()
                    def backup = origin + ".bak"
                    new File(origin).renameTo(new File(backup))
                    clean_file(backup, origin, violations)
                }
            }
        }
    
        clean_all("violations.xml")
    

    作为一般观察,System.out.println调用不一定是问题 - 可能是您的语句的格式为"Calling method on " + obj1 + " with param " + obj2 + " -> " + (obj1.myMethod(obj2)),实际成本是字符串连接(更好地使用StringBuffer / StringBuilder) )和方法调用的成本。

答案 2 :(得分:2)

修改

<强> 1。嵌套for循环已更正

<强> 2。 .java文件现在以递归方式提取

注意:

  

如果您对代码有信心,请替换第45行:open( hanw , "+>".$file.".txt" );

     

使用此行:open( hanw , "+>".$file );

<强> application.pl

use strict;
use File::Find qw( finddepth );
our $root = "src/";
our $file_data = {};
our @java_files;

finddepth( sub {
  if( $_ eq '.' || $_ eq '..' ) {
    return;
  } else {
    if( /\.java$/i ) {
      push( @java_files , $File::Find::name );
    }
  }
} , $root );

sub clean {
  my $file = shift;
  open( hanr , $file );
  my @input_lines = <hanr>;
  my $inside_for = 0;

  foreach( @input_lines ) {
    if( $_ =~ /(\s){0,}for(\s){0,}\((.*)\)(\s){0,}\{(\s){0,}/ ) {
      $inside_for++;
      push( @{$file_data->{$file}} , $_ );
    } elsif( $inside_for > 0 ) {
        if( $_ =~ /(\s){0,}System\.out\.println\(.*/ ) {
        } elsif( $_ =~ /(\s){0,}\}(\s){0,}/ ) {
          $inside_for--;
          push( @{$file_data->{$file}} , $_ );
        } else {
          push( @{$file_data->{$file}} , $_ );
        }
    } else {
      push( @{$file_data->{$file}} , $_ );
    }
  }
}

foreach ( @java_files ) {
  $file_data->{$_} = [];
  clean( $_ );
}

foreach my $file ( keys %$file_data ) {
  open( hanw , "+>".$file.".txt" );
  foreach( @{$file_data->{$file}} ) {
    print hanw $_;
  }
}

<强> data1.java

class Employee {
  /* code */
  public void Employee() {
    System.out.println("string");
    for( ... ) {
      System.out.println("string");
      /* code */
      System.out.println("string");
      for( ... ) {
        System.out.println("string");
        /* code */
        System.out.println("string");
      }
    }
  }
}

for( ... ) {
  System.out.println("string");
  /* code */
  System.out.println("string");
}

<强> data2.java

for( ... ) {
  /* code */
  System.out.println("string");
  /* code */
  System.out.println("string");
  /* code */
  for( ... ) {
    System.out.println("string");
    /* code */
    System.out.println("string");
    for( ... ) {
      System.out.println("string");
      /* code */
      System.out.println("string");
    }
  }
}

public void display() {
  /* code */
  System.out.println("string");
  for( ... ) {
    System.out.println("string");
    /* code */
    System.out.println("string");
    for( ... ) {
      System.out.println("string");
      /* code */
      System.out.println("string");
    }
  }
}

<强> data1.java.txt

class Employee {
  /* code */
  public void Employee() {
    System.out.println("string");
    for( ... ) {
      /* code */
      for( ... ) {
        /* code */
      }
    }
  }
}

for( ... ) {
  /* code */
}

<强> data2.java.txt

for( ... ) {
  /* code */
  /* code */
  /* code */
  for( ... ) {
    /* code */
    for( ... ) {
      /* code */
    }
  }
}

public void display() {
  /* code */
  System.out.println("string");
  for( ... ) {
    /* code */
    for( ... ) {
      /* code */
    }
  }
}

image-1

答案 3 :(得分:1)

所以你要解析Java。快速谷歌搜索显示javaparser,一个用Java编写的Java 1.5解析器。