版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangchao19890805/article/details/79758617
JPlag 是一个用于检查代码相似性的工具。主要用于教育领域,检测学生的代码作业是否有抄袭行为。假如存在两个学生:student1 和 student2。为这两个学生各自创建一个文件夹并把代码放到文件夹中。文件结构如下:
E:\ws\jplag\exercise1
|
├─ student1
| └─src
| └─Abc.java
|
└─ student2
└─src
└─Abc.java
student1 的 Abc.java 文件内容如下:
public class Abc {
public static void main(String[] args){
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
String a = "asfasdfasfd";
String b = "klsdjfl";
System.out.println(a + b);
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
}
private void m1 () {
int[] arr = new int[]{1,3,2,3};
int max = arr[0];
for (int i : arr) {
if (max < i){
max = i;
}
}
}
}
</body></html>
student2 的 java 文件内容如下:
public class Abc {
public static void main(String[] args){
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
int a = 1;
for (int i = 0; i < 10; i++) {
a ++;
}
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
}
private void m1 () {
int[] arr = new int[]{1,3,2,3};
int max = arr[0];
for (int i : arr) {
if (max < i){
max = i;
}
}
}
}
要使用 JPlag ,你需要去https://github.com/jplag/jplag/releases下载 JPlag 的 jar 文件。
我现在假设你已经下载好jar包,并把jar文件放到 E:\ws\jplag 。打开命令行,cd命令进入jar文件的目录,然后运行如下命令:
java -jar jplag-2.11.9-SNAPSHOT-jar-with-dependencies.jar -l java17 -r tmp -s exercise1
如果存在相似代码块,就会生成报告。当然你也可以利用程序执行上面的命令,这样可以方便的嵌入你的系统之中。
下面简单解释一下参数:
- -l: 使用的语言,这里的java17 表示 java 1.7 版本。
- -r:结果报告存放的目录地址。这里表示当前目录的tmp文件夹。
- -s:要进行检查的学生作业目录。
最后生成的结果以HTML文件的形式存放。HTML展示效果如下图所示:
看到这幅图,想必你也能发现问题了。JPlag生成的结果,不同的代码片段使用不同的颜色表示。用户希望用同一个颜色展示,此时我们需要对生成的结果进行处理。
进行处理的代码分成两个文件,分别是 Main.java 、 ColorUtils.java 和 TagUtils.java。其中 Main.java 包含主方法,ColorUtils.java 主要改变代码块的颜色,TagUtils.java 处理HTML标签。这三个文件在同一个包内,包名是zhangchao。
Main.java
package zhangchao;
import java.io.File;
public class Main {
public static void main(String[] args) {
ColorUtils.changeColor(new File("E:/ws/jplag/tmp/match0-0.html"));
ColorUtils.changeColor(new File("E:/ws/jplag/tmp/match0-1.html"));
}
}
ColorUtils.java
package zhangchao;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
/**
* 处理代码片段的颜色。
* @author 张超
*
*/
public final class ColorUtils {
/**
* 代码片段的颜色值。
*/
private final static String COLOR = "#ff0000";
/**
* 统一更改颜色
* @param file
*/
public static final void changeColor(final File file) {
FileInputStream fis = null;
BufferedReader br = null;
FileOutputStream fos = null;
BufferedWriter bw = null;
try {
fis = new FileInputStream(file);
br = new BufferedReader(new InputStreamReader(fis, "UTF8"));
String str = null;
StringBuilder htmlSb = new StringBuilder();
while(null != (str = br.readLine())){
htmlSb.append(str).append("\n");
}
String originHtml = htmlSb.toString(); // 读取的HTML
ArrayList<String> strList = new ArrayList<String>();
int scanIndex = 0;
while ( scanIndex < originHtml.length()) {
// 如果存在<Font>标签,就替换成统一的颜色。
if (TagUtils.isTag(originHtml, scanIndex)) {
scanIndex = TagUtils.increament(originHtml, scanIndex);
strList.add("<FONT color=\"" + COLOR + "\">");
} else {
strList.add(originHtml.substring(scanIndex, scanIndex + 1));
scanIndex++;
}
}
// 先关闭读取的流,再打开写入的流。
br.close();
br = null;
fis.close();
fis = null;
StringBuilder newHtmlSb = new StringBuilder();
for (String s : strList) {
newHtmlSb.append(s);
}
fos = new FileOutputStream(file);
bw = new BufferedWriter(new OutputStreamWriter(fos, "UTF8"));
bw.write(newHtmlSb.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != fis) {
fis.close();
}
if (null != br) {
br.close();
}
if (null != bw) {
bw.flush();
bw.close();
}
if (null != fos) {
fos.flush();
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
TagUtils.java
package zhangchao;
/**
* 处理HTML标签
* @author 张超
*
*/
public final class TagUtils {
static final String TAG_NAME = "font";
/**
* 是否有标签
* @param originHtml
* @param scanIndex
* @return
*/
public static final boolean isTag(final String originHtml, final int scanIndex){
if (originHtml.length() - scanIndex < TAG_NAME.length() + 2) {
return false;
}
if(("<"+TAG_NAME).equalsIgnoreCase(originHtml.substring(scanIndex, scanIndex + TAG_NAME.length() + 1))){
} else {
return false;
}
if (false == Character.isWhitespace(originHtml.charAt(scanIndex + TAG_NAME.length() + 1))) {
return false;
}
int tmpIndex = scanIndex + TAG_NAME.length() + 1;
boolean isInDoubleQuote = false; // 是否在双引号中
while(tmpIndex < originHtml.length()) {
char ch = originHtml.charAt(tmpIndex);
if (Character.isWhitespace(ch)) {
tmpIndex ++;
}
// 标签中出现 =" 表明在双引号中
else if (!isInDoubleQuote && ch == '=' && tmpIndex + 1 < originHtml.length() && originHtml.charAt(tmpIndex+1)=='"') {
tmpIndex += 2;
isInDoubleQuote = true;
}
else if (isInDoubleQuote && ch == '>') {
tmpIndex ++;
}
else if (isInDoubleQuote && ch=='"') {
tmpIndex ++;
isInDoubleQuote = false;
}
else if (!isInDoubleQuote && ch=='>') { // 标签结束 >
return true;
}
else {
tmpIndex ++;
}
}
return false;
}
/**
* 计算标签要跨过多少字符
* @param originHtml
* @param scanIndex
* @return
*/
public static final int increament(final String originHtml, final int scanIndex){
if (("<" + TAG_NAME + ">").equalsIgnoreCase(originHtml.substring(scanIndex, scanIndex) + TAG_NAME.length() + 2)) {
return TAG_NAME.length() + 2;
}
int tmpIndex = scanIndex + TAG_NAME.length() + 1;
boolean isInDoubleQuote = false; // 是否在双引号中
int originHtmlLength = originHtml.length();
while (tmpIndex < originHtmlLength) {
char ch = originHtml.charAt(tmpIndex);
if (Character.isWhitespace(ch)) {
tmpIndex ++;
}
// 标签中出现 =" 表明在双引号中
else if (!isInDoubleQuote && ch == '=' && tmpIndex + 1 < originHtmlLength && originHtml.charAt(tmpIndex+1)=='"') {
tmpIndex += 2;
isInDoubleQuote = true;
}
else if (isInDoubleQuote && ch == '>') { // 双引号中出现 > 不能作为标签结束的标志
tmpIndex ++;
}
else if (isInDoubleQuote && ch=='"') { // 找到右边的双引号,已经不在双引号里面了。
tmpIndex ++;
isInDoubleQuote = false;
}
else if (!isInDoubleQuote && ch=='>') { // img 标签结束 >
tmpIndex ++;
return tmpIndex;
}
// img 标签结束 />
else if (!isInDoubleQuote && ch=='/' && tmpIndex + 1 < originHtmlLength && originHtml.charAt(tmpIndex+1)=='>') {
tmpIndex += 2;
return tmpIndex;
}
else {
tmpIndex ++;
}
}
return tmpIndex;
}
}