使用 javascript 模拟 git diff 命令实现文本文件差异比较

news2024/11/24 6:03:35

diff.html:

<!DOCTYPE html>
<html>
<head>
    <title>文件比较</title>
    <meta charset="UTF-8">
</head>
<body>
<h1>文件比较</h1>
<form>
    <label for="file1">版本1:</label>
    <input type="file" id="file1" name="file1"><br><br>
    <label for="file2">版本2:</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修改修改为b
            var  sed1 = 0;
            if( index1 > 0 && index2 >0 ){
                sed1 = dp[index1 - 1][index2 - 1];
            }

            // v1:...a
            // v2: ... b
            // 此时,需要插入b
            var sed2 = 0;
            if( index2 >0 ){
                sed2 = dp[index1][index2 - 1];
            }

            // v1: ... a
            // v2:...b
            // 此时,需要删除a
            var 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_v1 );
                results.push( "+ " + line_v2 );
                index1--;
                index2--;
            }else if( sed == sed2 ){
                results.push( "+ " + line_v2 );
                index2--;
            }else if( sed == sed3 ){
                results.push( "- " + line_v1 );
                index1--;
            }
        }
    }

    while ( index1 >= 0 ){
        // v1 中多出的 "首行们" 都是需要删除的
        results.push( "- " + lines_v1[ index1 ] );
        index1--;
    }

    while ( index2 >= 0 ){
        // v2 中多出的 "首行们" 都是需要被插入的
        results.push( "+ " + lines_v2[ index2 ] );
        index2--;
    }

    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:a
                        dp[ index1 ][ index2 ] = 0;
                    }else {
                        // v1:a
                        // v2:b
                        dp[ 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:...a
                        dp[ index1 ][ index2 ] = dp[ index1 - 1 ][ index2 - 1 ];
                    }else {
                        // v1:...a
                        // v2:...b

                        // v1:... a
                        // v2:... b
                        // 此时 v1 的前部分和 v2的前部分做dp运算,a修改为b
                        var sed_prev1 = dp[ index1 - 1 ][ index2 - 1 ];

                        // v1: ... a
                        // v2:...b
                        // 此时v1的前部分和v2做dp运算,删除a
                        var sed_prev2 = dp[ index1 - 1 ][ index2 ];

                        // v1: ...a
                        // v2:  ... b
                        // 此时 v1和v2的前部分做dp运算,插入b
                        var 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>

doc_v1.txt:

盼望着,盼望着,东风来了,春天的脚步近了。
一切都像刚睡醒的样子,欣欣然张开了眼。
山朗润起来了,水涨起来了,太阳的脸红起来了。
小草偷偷地从土地里钻出来,嫩嫩的,绿绿的。
园子里,田野里,瞧去,一大片一大片满是的。
坐着,躺着,打两个滚,踢几脚球,赛几趟跑,捉几回迷藏。
风轻俏俏的,草软绵绵的。
桃树,杏树,梨树,你不让我,我不让你,都开满了花赶趟儿。
红的像火,粉的像霞,白的像雪。
花里带着甜味;闭了眼,树上仿佛已经满是桃儿,杏儿,梨儿。
花下成千成百的蜜蜂嗡嗡的闹着,大小的蝴蝶飞来飞去。
野花遍地是:杂样儿,有名字的,没名字的,散在草丛里像眼睛像星星,还眨呀眨。
“吹面不寒杨柳风”,不错的,像母亲的手抚摸着你,风里带着些心翻的泥土的气息,混着青草味儿,还有各种花的香,都在微微润湿的空气里酝酿。
鸟儿将巢安在繁花嫩叶当中,高兴起来,呼朋引伴的卖弄清脆的歌喉,唱出婉转的曲子,跟清风流水应和着。
牛背上牧童的短笛,这时候也成天嘹亮的响着。
雨是最寻常的,一下就是三两天。
可别恼。看,像牛牦,像花针,像细丝,密密的斜织着,人家屋顶上全笼着一层薄烟。
树叶却绿得发亮,小草也青得逼你的眼。傍晚时候,上灯了,一点点黄晕的光,烘托出一片安静而和平的夜。
在乡下,小路上,石桥边,有撑着伞慢慢走着的人,地里还有工作的农民,披着所戴着笠。
他们的房屋稀稀疏疏的,在雨里静默着。
天上的风筝渐渐多了,地上的孩子也多了。
城里乡下,家家户户,老老小小,也赶趟似的,一个个都出来了。
舒活舒活筋骨,抖擞抖擞精神,各做各的一份事儿去。
“一年之计在于春”,刚起头儿,有的是功夫,有的是希望 春天像刚落地的娃娃,从头到脚都是新的,它生长着。
春天像小姑娘,花枝招展的笑着走着。 春天像健壮的青年,有铁一般的胳膊和腰脚,领着我们向前去。

doc_v2.txt:

盼望着,盼望着,东风来了,春天的脚步进了。
一切都像刚睡醒的样子,欣欣然张开了眼。
山朗润起来了,水涨起来了,太阳的脸红起来了。
小草偷偷地从土地里钻出来,嫩嫩的,绿绿的。
园子里,田野里,瞧去,一大片一大片满是的。
坐着,躺着,打两个滚、踢几脚球,赛几趟跑、捉几回迷藏。
风轻巧巧的,草软绵绵的。
桃树,杏树,梨树,你不让我,我不让你,都开满了花赶趟儿。
红的像火,粉的像霞,白的像雪。
花里带着甜味;闭了眼,树上仿佛已经满是桃儿,杏儿,梨儿。
花下成千成百的蜜蜂嗡嗡的闹着,大小的蝴蝶非来非去。
野花遍地是:杂样儿,有名字的,没名字的,散在草丛里像眼睛像星星,还眨呀眨。
“吹面不寒杨柳风”,不错的,像母亲的手抚摸着你,风里带着些心翻的泥土的气息,混着青草味儿,还有各种花的香,都在微微润湿的空气里酝酿。
鸟儿将巢安在繁花嫩叶当中,高兴起来,呼朋引伴的卖弄风骚清脆的歌喉,唱出婉转的曲子,跟清风流水应和着。
牛背上牧童的断敌,这时候也成天嘹亮的响着。
雨是最寻常的,一下就是三两天。
可别恼。看,像牛牦,像花针,象细丝,密密的斜织着,人家屋顶上全笼着亿层薄烟。
树叶却绿得发亮,小草也青得逼你的眼。傍晚时候,上灯了,一点点黄晕的光,烘托出一片安静而和平的夜。
在乡下,小路上,石桥边,有撑着伞慢慢走着的人,地里还有工作的农民,披星戴月着所戴着笠。
他们的房屋稀稀疏疏的,在雨里静默着。
天上的风筝渐渐多了,地上的孩子也多了。
城里乡下,家家户户,老老小小,也赶趟似的,一各各都出来了。
舒活舒活筋骨,抖擞抖擞精神,各做各的一份事儿去。
"一年之计在于春",刚起头儿,有的是功夫,有的是希望 春天刚落地的娃娃,从头到脚都是,它生长着。
春天像小菇娘,花枝招展的笑着走着。 春天像键壮的青年,有铁一般的胳膊和腰脚,领着我们向前去。

测试效果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1288165.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微服务1 springcloud学习笔记P1-P40

b微服务技术栈_哔哩哔哩_bilibili 文档资料: 链接&#xff1a;https://pan.baidu.com/s/1P_Ag1BYiPaF52EI19A0YRw?pwdd03r 提取码&#xff1a;d03r 一 了解微服务技术 二 Eureka (1) Eureka配置 (2) 注册user-service (3) 总结 Ribbon 负载均衡 (1) 流程 三 nacos配置管理…

CFS三层靶机内网渗透

CFS三层靶机内网渗透 一、靶场搭建1.基础参数信息2.靶场搭建2.1网卡配置2.2Target1配置2.2.1 网卡配置2.2.2 Target1 BT配置 2.3Target2配置2.3.1 网卡配置2.3.2 Target2 BT配置 2.4Target3配置 二、内网渗透Target11.1信息收集1.1.1IP收集1.1.2端口收集1.1.3目录收集 1.2 webs…

思维模型 移情效应

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。情感迁移&#xff0c;爱屋及乌。 1 移情效应的应用 1.1 移情效应在市场营销中应用-多芬&#xff08;Dove&#xff09;“真美运动” 多芬&#xff08;Dove&#xff09;是一家知名的个人护理…

园区规划技术要点

&#xff08;一&#xff09;技术点介绍 1.WLAN&#xff1a;无线局域网WLAN&#xff08;Wireless Local Area Network&#xff09;是一种无线计算机网络&#xff0c;使用无线信道代替有线传输介质连接两个或多个设备形成一个局域网LAN&#xff08;Local Area Network&#xff09…

BUU UPLOAD COURSE 1

传一个cmd.php木马文件 访问一下这个图片地址 发现什么都没有&#xff0c;在hackbar里面连接一下我们的木马 然后看到了一些目录 然后直接查看flag就出来了 这里也可以用蚁剑去连接 直接访问地址&#xff0c;拿着地址去连接就行了。

从零开始,轻松实现Python接口自动化测试(基于PyCharm)

1.接口清单整理 &#xff08;1&#xff09;请求&#xff1a; 请求URL请求方法请求参数请求报文 &#xff08;2&#xff09;响应 状态码响应数据 2.用例设计 &#xff08;1&#xff09;单接口测试用例 模板&#xff1a;id、模块、接口名称、请求URL、用例名称、请求方法、…

用Python手把手教你WordCloud可视化

目录 WordCloud是什么&#xff1f; 具体使用 总结 WordCloud是什么&#xff1f; WordCloud是一种数据可视化技术&#xff0c;通过根据文本中单词的频率或权重来生成一个视觉上吸引人的词云图。在词云图中&#xff0c;单词的大小和颜色通常与其在文本中的出现频率相关&#…

【Java Web学习笔记】5 - XML

项目代码 https://github.com/yinhai1114/JavaWeb_LearningCode/tree/main/xml 零、在线文档 XML系列教程 一、XML引出 1.为什么需要XML 1.需求1 :两个程序间进行数据通信? 2.需求2:给一台服务器&#xff0c;做-一个配置文件&#xff0c;当服务器程序启动时&#xff0c;去…

synxflow 安装环境

介绍&#xff1a; 该软件可以动态模拟洪水淹没&#xff0c;滑坡跳动和泥石流使用多个cuda支持的gpu。它还提供了一个用户友好但多功能的Python界面&#xff0c;可以完全集成到数据科学工作流程中&#xff0c;旨在简化和加速危害风险评估任务。 这个包我从网上找到的资源特别特…

【WPF.NET开发】构造动态布局

本文内容 系统必备创建项目配置默认的 Grid Panel 控件向面板中添加控件测试布局汇总所有内容后续步骤 在动态定位中&#xff0c;您通过指定子元素相对于父元素应该如何排列以及应该如何包装来排列子元素。 您还可以将窗口和控件设置为在其内容扩展时自动扩展。 适用于 Vis…

Avalonia中使用Prism实现区域导航功能

前言 上一篇文章我们讲了在Avalonia开发中&#xff0c;引入Prism框架来完成项目的MVVM迁移。本章内容将带领大家学习如何在Avalonia中使用Prism框架实现区域导航功能。如果你还不知道Avalonia中如何引入Prism框架&#xff0c;请看我上一篇文章&#xff1a;Avalonia框架下面使用…

公有云迁移研究——AWS DMS

大纲 1 什么是DMS2 DMS的作用3 DMS在迁移的时候都做些什么4 在使用DMS的时候我们需要做些什么5 操作5.1 创建两个数据库终端节点5.2 创建迁移任务 6 可能遇到的问题7 总结 在本地机房或其他云往AWS上做迁移时&#xff0c;往往会遇到数据库迁移的任务。如果数据量不是特别大&…

【unity3D】Transform组件(如何访问和获取Transform组件)

&#x1f497; 未来的游戏开发程序媛&#xff0c;现在的努力学习菜鸡 &#x1f4a6;本专栏是我关于游戏开发的学习笔记 &#x1f236;本篇是unity的Transform组件 Transform组件 基础知识介绍三个成员变量常用属性扩展 Transform的相关查找方法静态方法 基础知识 介绍 在Unit…

ELK(一)—介绍

一、ELK介绍 ELK是指Elasticsearch、Logstash和Kibana&#xff0c;这三个开源软件构成了一个功能强大的日志管理和分析平台。Elasticsearch作为分布式搜索引擎&#xff0c;负责实时存储和检索大规模数据&#xff1b;Logstash用于从多个数据源采集、处理和传输日志数据&#xff…

分包(微信小程序)

首先&#xff0c;微信小程序中使用分包是为了减少首屏的请求&#xff0c;因为微信小程序会默认下载主包内的内容并展示到页面上&#xff0c;但是随着业务量的增加&#xff0c;代码量也会越来越大。会导致我们启动小程序的时候首页加载速度过慢的这个问题。这时我们就可以采用分…

dockerdesktop推送镜像到dockerhub

1.查看镜像(打开powershell) docker ps2.打tag docker tag pengzx/aspnetcoredocker:v1 pengzx/aspnetcoredocker:v2pengzx/aspnetcoredocker:v1:本地的镜像名加版本号 pengzx/aspnetcoredocker:v2&#xff1a;需要上传的镜像名&#xff08;要以dockerhub的用户名开头/本地镜像…

Data Linked UI

DataLinkedUl是一个Unity框架,它允许您在为您的应用程序创建用户界面时实现专业的数据驱动方法。使用此资产,您可以创建灵活的基于瓦片的任意大小的复杂接口系统。 核心功能: 灵活性-允许适应和调整数据变化,允许各种结构和功能配置,而不需要对现有系统进行重大破坏。 可伸…

HTTP之跨域

HTTP之跨域 跨域&#xff08;Cors&#xff09;两种请求简单请求浏览器不同的处理方式Access-Control-Allow-OriginAccess-Control-Allow-CredentialswithCredentials属性 非简单请求服务器回应&#xff1a;什么时候会触发OPTIONS&#xff08;预检请求&#xff09;呢&#xff1f…

ubuntu 20.04 server 安装 zabbix

ubuntu 20.04 server 安装 zabbix 参考文档 https://zhuanlan.zhihu.com/p/587415883?utm_id0 https://repo.zabbix.com/zabbix/6.0/ubuntu/pool/main/z/zabbix-release/ https://blog.csdn.net/ammc520/article/details/134279322 在Ubuntu 20.04上安装MySQL教程 https://b…

高防IP是什么? 防护CC 对抗DDOS

什么是DDoS高防IP&#xff1f; DDoS&#xff08;分布式拒绝服务&#xff09;攻击是指攻击者通过利用大量恶意流量向目标服务器发送请求&#xff0c;导致目标服务器无法正常处理合法用户的请求。DDoS高防IP是一种通过技术手段来应对DDoS攻击的解决方案。它能够过滤掉恶意流量&a…