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

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:

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

测试效果:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python中读写CSV文件的深入探讨

目录 一、引言 二、如何读取CSV文件 三、如何写入CSV文件 四、处理大型CSV文件 五、总结 一、引言 CSV&#xff08;Comma-Separated Values&#xff09;文件是一种常见的逗号分隔值格式的文件&#xff0c;常用于存储和传输数据。在Python中&#xff0c;我们可以使用内置的…

手机充电器市场分析:预计2028年将达到82亿美元

在5G时代飞速发展的今天&#xff0c;随着科技的进步、应用的发展以及人们对以智能手机、平板电脑、智能穿戴设备为代表的智能终端设备追求越来越高的品质和功能&#xff0c;智能终端设备产品的更新换代的速度越来越快&#xff0c;这也将给全球智能终端充储电产品市场带来更大的…

计算机基础知识64

ForeignKey属性 to&#xff1a;设置要关联的表 related_name&#xff1a; 反向操作时&#xff0c;使用的字段名&#xff0c;用于代替原反向查询时的’表名_set’ related_query_name:反向查询操作时&#xff0c;使用的连接前缀&#xff0c;用于替换表名 to_field:设置要关联的表…

【黑马甄选离线数仓day09_会员主题域开发_DWD和DWM层】

拉链表流程 1.从ODS层获取增量数据(上一天新增和更新的数据) 2.拿着DWD原始拉链表数据 left join 增量数据 ,修改原始拉链中历史数据的结束时间 3.拿着left join 的结果集 union all 增量数据 4.把最新的拉链数据优先保存到DWD对应的临时表中 5.使用insertselect 方式把临时表中…

8000元投影仪值得买吗?选择一步到位当贝X5 Ultra不用担心白天看不见

随着生活方式的变化&#xff0c;现在越来越多家用将家里的电视机换成屏幕更大的投影仪&#xff0c;投影仪似乎要逐渐取代电视机在家庭中的位置&#xff1b;然而近日在抖音刷到河南广播电视台民生频道的一个视频&#xff1a;河南郑州一小伙花了八千多买了极米投影仪&#xff0c;…

Scala 从入门到精通

Scala 从入门到精通 数据类型 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http:…

LeetCode [中等]全排列(回溯算法)

46. 全排列 - 力扣&#xff08;LeetCode&#xff09; 回溯法 采用试错的思想&#xff0c;它尝试分步的去解决一个问题。在分步解决问题的过程中&#xff0c;当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候&#xff0c;它将取消上一步甚至是上几步的计算&…

怎么对文件加密?文件加密软件操作保姆式演示!

大家是不是遇到过这种情况&#xff0c;文件一个不小心就被别人轻易外发出去&#xff0c;并且还是特别重要的内容。在企业中这种现象已经非常常见啦。 今天就给友友们&#xff0c;分享一款神器&#xff0c;它可以让你点点鼠标&#xff0c;就对企业的重要文件进行加密。 1、获取…

学习php中使用composer下载安装firebase/php-jwt 以及调用方法

学习php中使用composer下载安装firebase/php-jwt 以及调用方法 1、安装firebase/php-jwt2、封装jwt类 1、安装firebase/php-jwt composer require firebase/php-jwt安装好以后出现以下文件: 2、封装jwt类 根据所使用的php框架&#xff0c;在指定目录创建 Token.php <?ph…

llama.cpp部署(windows)

一、下载源码和模型 下载源码和模型 # 下载源码 git clone https://github.com/ggerganov/llama.cpp.git# 下载llama-7b模型 git clone https://www.modelscope.cn/skyline2006/llama-7b.git查看cmake版本&#xff1a; D:\pyworkspace\llama_cpp\llama.cpp\build>cmake --…

Angular 进阶之四:SSR 应用场景与局限

应用场景 内容丰富&#xff0c;复杂交互的动态网页&#xff0c;对首屏加载有要求的项目&#xff0c;对 seo 有要求的项目&#xff08;因为服务端第一次渲染的时候&#xff0c;已经把关键字和标题渲染到响应的 html 中了&#xff0c;爬虫能够抓取到此静态内容&#xff0c;因此更…

修改python打包后的窗体图标、任务栏图标、exe图标

前言 我python开发的GUI界面(图形用户界面)一直是tkinter&#xff0c;打包exe一直是Pyinstaller。但是打包出来的exe图标、状态栏图标、窗体左上角图标一直是默认的羽毛&#xff0c;我想自定义。 效果 最后使用base64创建临时ico解决了该问题 步骤 创建icoToBase64.py&am…

如何使用 Oracle SQL Developer 连接 pgvector

如何使用 Oracle SQL Developer 连接 pgvector 1. 下载 postgresql 的 jdbc 驱动2. Oracle SQL Developer 配置第三方驱动3. Oracle SQL Developer 配置 postgres 连接 1. 下载 postgresql 的 jdbc 驱动 访问 https://jdbc.postgresql.org/download/&#xff0c;下载驱动&…

09.复刻ChatGPT,自我进化,AI多智能体

文章目录 复刻ChatGPT原因准备开整ALpacaVicuna GPT-4 EvaluationDolly 2.0其他合集Self-improve 自我进化表现形式法1&#xff1a;自我催眠法2&#xff1a;Agent交互法3&#xff1a;ReasonAct AI多智能体AI规划角色的一天加入亿点点细节&#xff08;外界刺激&#xff09;Refle…

STM32存储左右互搏 SPI总线读写FRAM MB85RS16

STM32存储左右互搏 I2C总线读写FRAM MB85RS16 在中低容量存储领域&#xff0c;除了FLASH的使用&#xff0c;&#xff0c;还有铁电存储器FRAM的使用&#xff0c;相对于FLASH&#xff0c;FRAM写操作时不需要预擦除&#xff0c;所以执行写操作时可以达到更高的速度&#xff0c;其…

【教程】苹果推送证书的创建和使用流程详解

摘要 本篇博客主要介绍了苹果推送证书的使用流程。首先&#xff0c;在苹果开发者中心创建推送证书&#xff0c;然后在应用程序中使用该证书进行消息推送。文章详细说明了创建推送证书的步骤&#xff0c;并提供了在应用程序中注册推送服务、发送推送消息以及处理推送消息的相关…

深入浅出理解kafka ---- 万字总结

1.Kafka简介 Kafka 本质上是一个 MQ&#xff08;Message Queue&#xff09;&#xff0c;使用消息队列的优点&#xff1a; 解耦&#xff1a;允许独立的扩展或修改队列两边的处理过程。可恢复性&#xff1a;即使一个处理消息的进程挂掉&#xff0c;加入队列中的消息仍然可以在系…

赛氪网荣膺地理标志语言服务教育与实践基地联盟理事会员单位

随着地理标志产品推介需求的持续扩大&#xff0c;知识产权保护和语言服务行业面临着新的挑战和机遇。在这个背景下&#xff0c;知识产权出版社指导下的地理标志语言服务教育与实践基地联盟应运而生&#xff0c;旨在推动地理标志产品的推广和知识产权保护。赛氪网作为项目运营方…

单行文本溢出,多行文本溢出的省略样式

单行文本溢出 效果&#xff1a; html <div>你好呀&#xff01;你好呀&#xff01;你好呀&#xff01;你好呀&#xff01;你好呀&#xff01;你好呀&#xff01;你好呀&#xff01;你好呀&#xff01;你好呀&#xff01;</div> css: <style>div{width: 300p…

Python中的内省与反射机制及其应用场景

1. 概述 在计算机学中&#xff0c;反射式编程&#xff08;英语&#xff1a;reflective programming&#xff09;或反射&#xff08;英语&#xff1a;reflection&#xff09;&#xff0c;是指计算机程序在运行时&#xff08;runtime&#xff09;可以访问、检测和修改它本身状态或…