任何方法都可以轻松地将它们从文件中的 for 循环块中删除......
之前:
for( ... ) {
...
System.out.println("string");
...
System.out.println("string");
...
}
之后:
for( ... ) {
...
...
...
}
任何方法都可以轻松地将它们从文件中的 for 循环块中删除......
之前:
for( ... ) {
...
System.out.println("string");
...
System.out.println("string");
...
}
之后:
for( ... ) {
...
...
...
}
这很棘手:哪个右括号关闭了for
-loop?要么解析整个代码,要么使用一些启发式方法。在下面的解决方案中,我要求右大括号的意图与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 系统上,分隔符必须更改为"
. 此外,我们必须自己做所有的 globbing。
> 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
这是我在评论中概述的括号计数算法的实现。该解决方案也可以进行备份。命令行参数将被解释为 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 条语句;但这些脚本无法做到这一点。
我建议使用静态代码分析器PMD来定位问题陈述和删除行的简单脚本的双重方法。所有源代码和配置都包含在下面,编辑包括 Python 和 Groovy 替代方案。
PMD 有一个扩展机制,允许使用简单的 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"]]
使用这种方法的好处是:
System.out.println
循环内的条件 - 到任何深度指示
在自定义规则集中创建 PMD 规则。
rulesets/java
在某处创建一个目录CLASSPATH
——如果是“.”,它可能在工作目录下。正在路上。在此目录中,创建一个名为 XML 的规则集文件,custom.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”
在您的代码库上运行 PMD,只选择您的规则集 java-custom。使用 XML 报告获取开始行和结束行。在文件“violations.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 是否已识别出正确的语句。
创建一个 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])
运行 Python 脚本。这将通过添加“.bak”来重命名匹配的文件,然后重写 Java 文件而不使用违规行。这可能具有破坏性,因此请确保首先正确备份您的文件。特别是,不要连续运行脚本两次 - 第二轮将天真地删除相同的行号,即使它们已经被删除:
$ python remover.py violations.xml
编辑
对于那些更喜欢使用更面向 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 更好)和方法调用的成本。
编辑:
1. 嵌套for
循环更正
2..java
文件现在递归获取
笔记:
当您对代码有信心时,请替换第 45 行:
open( hanw , "+>".$file.".txt" );
用这条线:
open( hanw , "+>".$file );
应用程序.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 $_;
}
}
数据1.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");
}
数据2.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 */
}
数据2.java.txt
for( ... ) {
/* code */
/* code */
/* code */
for( ... ) {
/* code */
for( ... ) {
/* code */
}
}
}
public void display() {
/* code */
System.out.println("string");
for( ... ) {
/* code */
for( ... ) {
/* code */
}
}
}
所以你正在寻找解析Java。快速的 Google 搜索显示javaparser,一个用 Java 编写的 Java 1.5 解析器。