最小java文件

vd8tlhqk  于 2021-07-06  发布在  Java
关注(0)|答案(3)|浏览(373)

我想比较两个.java文件,只检查它们是否相同。
例如,我认为以下两个代码块是相同的

public class A extends B {
private int i;
private int j;
}

public class A extends   B {

private int i;

private int j;

}

因为我不在乎“压缩”的代码是否仍然可以编译,所以我想删除所有空格和换行符,然后比较文件。-会不会导致误报比如有没有一个换行符可以改变代码的工作方式,而我却想不出来?
我还没有研究的另一种方法是用javaparser解析文件——但是还没有比较编译单元的经验,而且可能比第一种方法慢。

h22fl7wq

h22fl7wq1#

相同是什么意思?如果你澄清了这个要求,剩下的问题就变得简单了。

相同意味着相同的非空白字符顺序相同。

去掉所有空白,然后比较。这将使像“”这样的有效字符串与像“”和“”这样的字符串匹配     "

相同意味着忽略所有不重要的空白。

剥离空白是行不通的,您需要解析文件以知道哪个空白是可替换的;然后,您可以删除或压缩具有特定模式的可替换空白,并比较文件。这意味着 int getMaxX() { int x = 3; return x; } 将不同于 int getMaxX() { return 3; } ####相同意味着代码流是相同的。
您可以编译这两个文件,并比较它们的 .class 文件夹。这使得比较逻辑流更容易;但是,在变量名不同的情况下仍然需要小心。这将无法显示 if (x) { doTrue(); } else { doFalse(); } 与…相同 if (!x) { doFalse(); } else { doTrue(); } 因为即使逻辑是相同的,代码流也是不同的。

相同意味着输出的结果是相同的。

您可以编写一组单元测试,在特定条件下执行代码;验证它们是否产生相同的结果。由于所涉及的工作,这不是一个完美的“完全相同”的方法,因为没有无限的时间,不可能对非平凡的方法进行彻底的测试。

如果您正在构建一个代码复制查找器,那么下面的算法效果最好。

从将文件正确解析为抽象语法树的逻辑开始。
对于树中的每个节点,根据节点的“相等”特性创建一个哈希值(从上面可以看出,相等并不总是绝对的,因为您可能希望具有变量重命名的相同方法匹配或不匹配。)
比较两个根节点(对于两个输入)的哈希相等性。如果它们不同,则不需要额外的工作,它们是不同的。如果它们完全相同,请检查这些文件(因为您很可能使用了错误的哈希算法)是否存在假阳性匹配。
用上面的方法判断什么是相同的可能会变得很棘手,需要仔细设计散列算法,对被认为相同的任何两个项进行相同的散列。例如:
散列(条件(x>0))=3523
哈希(条件(0<x))=3523
可能对一种“相同”有效,但对另一种无效。
这种方法之所以能很好地工作,是因为它相对容易指定对相同定义的细粒度控制;而且,哈希的使用使得搜索相同的子元素变得容易。
现在,如果您不想写这个,您可以使用其他人的相同定义,使用复制粘贴检测器for javahttps://pmd.github.io/latest/pmd_userdocs_cpd.html

0wi1tuuw

0wi1tuuw2#

我想删除所有空格和换行符,然后比较文件。-会导致误报吗?
当然会。

public int foo() {}
public intf oo() {}

从语义上讲是完全不同的,但如果去掉空格则相等。然而:

public int foo() {;}
public int foo() {}

在语义上完全相同。他们也是:

public int[] foo() {}
public int foo() [] {} // yeah this is legal java syntax.

它们不仅在语义上是相同的;大多数AST(ecj或javac的解析器阶段发出的源代码的树状表示)实际上无法区分这两行代码;即使是保留语法的漂亮打印机也总是会发出上述两种方法中的第一种,即使您是用第二种(无可否认,不是风格上的首选)方式编写的。
基本的文本分析永远不会让你达到目的。java语法并不是那种只需几个regex和replace操作就可以产生某种可以推理的语法。你需要一个完整的分析工作。
我看到两个选项:
将源文件编译成类文件,并进行比较。不仅仅是逐字节的,你需要确保类文件包含你想要的信息(比如参数名),但是忽略了你不想要的信息(比如行符号;假设您不关心是否有人在文件中抛出一个空行,但这会修改linenumber表)。但是,类文件比源文件更易于分析。
使用ecj或各种解析器库的java语法并比较ast。这是相当复杂的,但唯一真正正确的答案,因为它是迄今为止最灵活的:对于任何可以想象的语法变化,你可以精确地定义什么是相关的,什么是不相关的。

1的一些主要问题是,有些语法差异在类文件中并不重要,因此无法区分它们。这可能更像是一个“特性”而不是一个“bug”,但是您还没有解释为什么要比较java代码,所以我不知道。它当然关闭了那扇门:如果你沿着这条路走下去,你将永远无法检测到任何语法上的差异,这些差异最终不会出现在类文件中,如果不彻底重写项目的话。“不影响类文件的代码”的一个明显候选:注解。还有,任何带有 RetentionLevel.SOURCE . 他们只是。。消失,所以任何基于类文件的比较系统都无法分辨。

注意:将其边界字符都是java标识符合法的任何空格减少到一个空格,并将其中一个或两个边界字符都不是的任何空格(因此,文件的开始/结束、括号、括号、破折号、点等等)减少到零,至少比直接向上“去除所有空格”更好,但这对于数组括号的后缀语法来说是不够的 [] 在方法签名上,在方法签名、注解和, \u 在字符串中转义,还有更多的事情导致了不同的源代码,但在我能想象的几乎所有方面都是相关的,100%等价的。

ergxz8rk

ergxz8rk3#

我想可能有以下两种方法
您可以删除所有的换行符和空格,然后生成两个字符串的md5哈希并进行比较。
如果你不想在格式上松懈,你可以一个字符一个字符地读取文件并比较它,而比较你可以忽略如果你得到一些额外的空间,后面是一个匹配的空间,并试图匹配下一个字符与当前字符。

相关问题