WinMerge.html:
<!DOCTYPE html>
<html>
<head><title>WinMerge</title><meta charset="UTF-8">
</head>
<body>
<h1>文件比较</h1>
<form><label for="file1">旧版本:</label><input type="file" id="file1" name="file1"><br><br><label for="file2">新版本:</label><input type="file" id="file2" name="file2"><br><br><button type="button" onclick="compare()">开始比较</button>
</form><div id = "div_info" style="width:1000px;height:500px;border:1px solid gray;overflow:auto">
</div><script>var infoDiv = document.getElementById("div_info");function compare() {var file1 = document.getElementById("file1").files[0];var file2 = document.getElementById("file2").files[0];var reader1 = new FileReader();var reader2 = new FileReader();reader1.readAsText(file1);reader2.readAsText(file2);var lines_v1 = null;var lines_v2 = null;reader1.onload = function() {lines_v1 = reader1.result.split('\n');// printArray( lines_v1 );reader2.onload = function() {lines_v2 = reader2.result.split('\n');// printArray( lines_v2 );// 比较两个文档的区别docDiff( lines_v1,lines_v2 );}}
}function printArray( array ){var len = array.length;for (let i = 0; i < len; i++){console.log( "[" + array[ i ] + "]" );}
}function docDiff( lines_v1,lines_v2 ){var dp = calculateShortestEditDistance(lines_v1, lines_v2);var index1 = lines_v1.length - 1;var index2 = lines_v2.length - 1;console.log("一共需要" + dp[ index1 ][ index2 ] + "步编辑操作");var results = [];while ( index1 >= 0 && index2 >= 0 ){var line_v1 = lines_v1[ index1 ];var line_v2 = lines_v2[ index2 ];if( line_v1 == line_v2 ){// v1:...a// v2:...a// 原封不动的输出results.push( " " + line_v1 );index1--;index2--;}else {// v1:...a// v2:...b// v1:... a// v2:... b// 此时,a修改修改为bvar sed1 = 0;if( index1 > 0 && index2 >0 ){sed1 = dp[index1 - 1][index2 - 1];}// v1:...a// v2: ... b// 此时,需要插入bvar sed2 = 0;if( index2 >0 ){sed2 = dp[index1][index2 - 1];}// v1: ... a// v2:...b// 此时,需要删除avar sed3 = 0;if( index1 > 0 ){sed3 = dp[index1-1][index2];}var sed = Math.min( sed1,sed2,sed3 );if( sed == sed1 ){// results.add( "edit " + line_v2 + " DIFF:" + StringDiffTest.diff( line_v1,line_v2 ) );// var diffInfo = StringDiffTest.diff(line_v1, line_v2);// results.add( "edit " + diffInfo );results.push( "+ " + line_v2 );results.push( "- " + line_v1 );index1--;index2--;}else if( sed == sed3 ){results.push( "- " + line_v1 );index1--;}else if( sed == sed2 ){results.push( "+ " + line_v2 );index2--;}}}while ( index2 >= 0 ){// v2 中多出的 "首行们" 都是需要被插入的results.push( "+ " + lines_v2[ index2 ] );index2--;}while ( index1 >= 0 ){// v1 中多出的 "首行们" 都是需要删除的results.push( "- " + lines_v1[ index1 ] );index1--;}for ( var i=results.length -1;i>=0;i-- ){var line = results[ i ];var small = document.createElement( "small" );small.innerHTML = line;if( line.startsWith( "-" ) ){small.style.color = "red";}else if( line.startsWith( "+" ) ){small.style.color = "green";}infoDiv.appendChild( small );infoDiv.appendChild( document.createElement( "br" ) );}
}// 返回 int[][]
function calculateShortestEditDistance( lines_v1,lines_v2 ){// dp[i][j] 表示的是将 lines_v1 的前i个元素变换为 lines_v2 中的前j个元素需要使用的最优( 即需要转换步骤最少 )的转换方式var size_v1 = lines_v1.length;var size_v2 = lines_v2.length;var dp = createArray( size_v1,size_v2 );for (var index1 = 0; index1 < size_v1; index1++) {var line_v1 = lines_v1[ index1 ];for (var index2 = 0; index2 < size_v2; index2++) {var line_v2 = lines_v2[ index2 ];if( index1 == 0 ){if( index2 == 0 ){if( line_v1 == line_v2 ){// v1:a// v2:adp[ index1 ][ index2 ] = 0;}else {// v1:a// v2:bdp[ index1 ][ index2 ] = 1;}}else {if( contains( lines_v2,line_v1,0,index2 ) ){// v1: a// v2:...a... size = index2 + 1// v1转换为 v2需要 size - 1步( 也就是 index2步 )插入操作dp[ index1 ][ index2 ] = index2;}else {// v1: a// v2:...b... size = index2 + 1// v1转换为 v2需要 1步编辑操作,size-1= index2 步插入操作,一共index2 + 1步操作dp[ index1 ][ index2 ] = index2 + 1;}}}else {if( index2 == 0 ){if( contains(lines_v1, line_v2, 0, index1) ){// v1:....a... size = index1 + 1// v2: a// v1转换为 v2需要 size-1=index1步删除操作dp[ index1 ][ index2 ] = index1;}else {// v1:....a... size = index1 + 1// v2: b// v1转换为 v2需要 1步编辑操作和size-1=index1步删除操作,一共index1+1步操作dp[ index1 ][ index2 ] = index1 + 1;}}else {if( line_v1 == line_v2 ){// v1:...a// v2:...adp[ index1 ][ index2 ] = dp[ index1 - 1 ][ index2 - 1 ];}else {// v1:...a// v2:...b// v1:... a// v2:... b// 此时 v1 的前部分和 v2的前部分做dp运算,a修改为bvar sed_prev1 = dp[ index1 - 1 ][ index2 - 1 ];// v1: ... a// v2:...b// 此时v1的前部分和v2做dp运算,删除avar sed_prev2 = dp[ index1 - 1 ][ index2 ];// v1: ...a// v2: ... b// 此时 v1和v2的前部分做dp运算,插入bvar sed_prev3 = dp[ index1 ][ index2 - 1 ];dp[ index1 ][ index2 ] = Math.min( sed_prev1,sed_prev2,sed_prev3 ) + 1;}}}}}return dp;
}// todo 测试行列是否写反了
function createArray(rowCount, colCount) {var arr = [];for (var i = 0; i < rowCount; i++) {arr[i] = [];for (var j = 0; j < colCount; j++) {arr[i][j] = 0;}}return arr;
}function contains(lines, targetLine, beginIndex, endIndex) {for (var i = beginIndex; i <=endIndex ; i++) {if( lines[ i ] == targetLine ){return true;}}return false;
}
</script>
</body>
</html>
测试效果: