【113】JPlag 重複程式碼段顏色不一致問題的解決方法。
阿新 • • 發佈:2018-12-26
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;
}
}
}
}
我現在假設你已經下載好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;
}
}