实现 两个文本匹配不同-高亮显示 适合小中型(2w字内)篇幅文文章

   应同学的公司需求,需要对比源文件录音文件之间的文字差别,将配音文件与源文件不同的地方进行高亮显示,于是决定花半天的时间帮他写出匹配功能的网页。

先看成果截图:
在这里插入图片描述

当然你也可以自己体验一下:
在这里插入图片描述

文件1: 下载测试源文件

文件2: 下载测试待配对文件

地址: 演示地址
上代码:
html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <title>文件比较</title>
<style type="text/css">
@import "../js/layui/css/layui.css";
</style>
        <style type="text/css">


        </style>
</head>
<script src="../js/layui/layui.js"></script>
<script src="../js/jquery_need.js"></script>
<!-- <script src="../js/layui/lay/modules/layer.js"></script> -->
<body style="margin: 0;" class="layui-nav" >
    <ul class="layui-nav">
        <li class="layui-nav-item">
          <a>文档匹配<span class="layui-badge">+</span></a>
        </li>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <li class="layui-nav-item">
            <a id="name1"></a>
          </li>
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
          <li class="layui-nav-item">
            <a  id="name2"></a>
          </li>
      </ul>
    <div style="width: 100vwpx;margin: 0 auto;height: 600px;">
        <div class="layui-row " style="color: black;font-family: '幼圆';">
            <div class="layui-col-md5">
              <div id="filecon1" style="word-break: break-all;word-wrap: break-word;padding: 15px;width:80%;height:600px;margin: 0  auto;overflow-y: auto;border-color: red;border-width: 1px;border-style: solid;background-color: white;box-shadow: 0 0 10px 1px rgba(255,0,0,0.09);border-radius: 5px;"></div>
            </div>
            <div class="layui-col-md5">
                <div  id="filecon2" style="word-break: break-all;word-wrap: break-word;padding: 15px;width:80%;height:600px;margin: 0  auto;overflow-y: auto;border-color: red;border-width: 1px;border-style: solid;background-color: white;box-shadow: 0 0 10px 1px rgba(255,0,0,0.09);border-radius: 5px;position: relative;left: -60px;"></div>
            </div>
            <div class="layui-col-md2">
                <button type="button" class="layui-btn" id="start">开始匹配</button>
                <br><br>
                <div style="margin: 0 0 0 -20px;">
                    <font style="color: white;">高亮背景颜色</font>
                    <div id="bgcolor_test"></div>
                    <a hidden id="bgcolor">black</a>
                    <a id="bgcolor_" style="width: 2px;height: 2px;background-color: black;">&nbsp;&nbsp;&nbsp;</a>
                </div>
                <br>
                <div style="justify-content: center;display:flex;margin: 0 auto;position: relative;width: 300px;left: -40px;">
                    <label  style="color: white;" class="layui-form-label">字体大小</label>
                    <div class="layui-input-block" style="width: 50px;justify-content: center;display:flex;margin: 0 auto;position: relative;position: relative;left: -60px;">
                      <input type="text" id="fontSize" name="title" required  lay-verify="required" value="15" autocomplete="off" class="layui-input">
                    </div>
                </div>
                <br>
                <div class="layui-form-item">
                    <!-- <label class="layui-form-label">短输入框</label> -->
                    <div class="layui-input-inline">
                        <label class="layui-form-label" style="color: white;margin: -10px 0 0 -60px;">编码格式</label>
                        <div class="layui-input-block" style="color: white;margin: 0px 0 0 -40px;">
                            
                          <input type="radio" name="code" value="GBK" title="GBK" checked>GBK
                          <input type="radio" name="code" value="UTF-8" title="UTF-8" >UTF-8
                        </div>
                      </div>
                      <div class="layui-input-inline">
                        <input type="text" style="position: relative;left: -40px;" name="username" lay-verify="required" placeholder="间隔次数长度+60"  autocomplete="off" class="layui-input">
                      </div>
                      <br><br><br>
                      <div class="layui-input-inline">
                        <input type="text" style="position: relative;left: -40px;" name="username" lay-verify="required" placeholder="positionCril-4000"  autocomplete="off" class="layui-input">
                      </div>
                      <br><br><br>
                      <div class="layui-input-inline">
                        <input type="text" style="position: relative;left: -40px;" name="username" lay-verify="required" placeholder="精确度-Lv10"  autocomplete="off" class="layui-input">
                      </div>
                      <br><br><br>
                      <div class="layui-input-inline">
                        <input type="text" style="position: relative;left: -40px;" name="username" lay-verify="required" placeholder="精确度截距-0.5"  autocomplete="off" class="layui-input">
                      </div>
                      <br><br><br>
   
                      <br><br><br>
                      <div class="layui-input-inline">
                        <img src="../img/myWechat.jpg" style="width: 100%;height: 100%;position: relative;left: -40px;top: 20px;" alt="">
                      </div>
                      
                  </div>
                  <div id="test1" class="demo-transfer"></div>
              </div>
          </div>
          <div style="height: 5px;"></div>
          <div class="layui-row">
            <div class="layui-col-md5" >
                <button id="btn1" type="button" class="layui-btn layui-btn-danger" style="justify-content: center;display: flex;margin: 0 auto;">点击选择文件1</button>
                <input type="file" hidden id="file1" οnchange="readFiles(this,'1')"/>
            </div>
            <div class="layui-col-md5">
                <button type="button" id="btn2" class="layui-btn layui-btn-danger" style="justify-content: center;display: flex;margin: 0 auto;position: relative;left: -60px;">点击选择文件2</button>
                <input type="file" hidden id="file2" οnchange="readFiles(this.files,2)"/>
            </div>
            <div class="layui-col-md2" style="position: relative;left: -60px;">
                说明,可能由于算法原因,会存在那么一些2%左右的文字会不见,会逐渐改善。有啥问题请扫二维码进行咨询。包括功能的改善...
              </div>
          </div>
    </div>



</body>
</html>

JavaScript


<script>
    $(document).ready(function(){  
       
    });
    
          
    layui.use('colorpicker', function(){
        var colorpicker = layui.colorpicker;
        //渲染
        colorpicker.render({
            elem: '#bgcolor_test'  //绑定元素
            ,change: function(color){
                // alert(color)
                $("#bgcolor").html(color);
                $("#bgcolor_").css("background-color",color);
            }
         })
         
         
    });
    layui.use('layer', function(){
        var  layer = layui.layer;
        
    // layer.msg('请先选择好文件!');
    }); 
  //无需再执行layui.use()方法加载模块,直接使用即可
  var control_lock = 0;
  var form = layui.form
    ,layer = layui.layer;
    $("#btn1").click(function(){
        $("#file1").click();
    })
    $("#btn2").click(function(){
        $("#file2").click();
    })
    function readFiles(file,type){
        // alert("A");
       
    }
    $("#file1").change(function(e) {
       
        var fileObj = document.getElementById('file1');
        var file = fileObj.files[0];
        // alert(file.type);
        if(file.type.indexOf("text") == -1){
                // alert("只能打开text文本");
                layer.msg("只能打开text文本");
                return ;
        }
        var reader = new FileReader();
        layui.use('layer', function(){
            var  layer = layui.layer;
            layer.load();
        // layer.msg('请先选择好文件!');
        }); 
        reader.onload = function() {
            // alert("?凄凄惨惨"+this.result);
            $("#filecon1").empty();
            $('#filecon1').append(this.result);
            $("#name1").html(file.name);
            layui.use('layer', function(){
                var  layer = layui.layer;
                layer.closeAll();
            // layer.msg('请先选择好文件!');
            }); 
        }
        var chkRadio = $('input:radio[name="code"]:checked').val();

        reader.readAsText(file,chkRadio);
        
    });
    
    $("#file2").change(function(e) {
        
        var fileObj = document.getElementById('file2');
        var file = fileObj.files[0];
        var reader = new FileReader();
        
        if(file.type.indexOf("text") == -1){
                // alert("只能打开text文本");
                layer.msg("只能打开text文本");
                return ;
        }
        layui.use('layer', function(){
            var  layer = layui.layer;
            layer.load();
        // layer.msg('请先选择好文件!');
        }); 
        reader.onload = function() {
            // alert("?凄凄惨惨"+this.result);
            $("#filecon2").empty();
            $('#filecon2').append(this.result );
            control_lock = 0;
            $("#name2").html(file.name);
            layui.use('layer', function(){
                var  layer = layui.layer;
                layer.closeAll();
            // layer.msg('请先选择好文件!');
            }); 
        }
        var chkRadio = $('input:radio[name="code"]:checked').val();
        reader.readAsText(file,chkRadio);
        
    });
    $("#start").click(function(){
        // alert($('#filecon1').html().length);
        if($('#filecon2').html().length == 0 || $('#filecon1').html().length == 0){
            layui.use('layer', function(){
                var layer = layui.layer;
                
                layer.msg('请先选择好文件!');
                });     
                return ;
            }
            //可以进行筛选了
            // layer.msg("开始筛选...");
            if(control_lock == 1){
                layui.use('layer', function(){
                var layer = layui.layer;
                
                layer.msg('已经匹配!');
                });     
                return ;
            }
            //判断大小。
            var digit = ["1","2","3","4","5","6","7","8","9","0"];
            for(var i = 0;i< $("#fontSize").val().length;i++){
                // alert($("#fontSize").val().charAt(i));
                if(digit.indexOf($("#fontSize").val().charAt(i)) == -1){
                    $("#fontSize").val(15);
                    break;
                }
            }
            // alert($("#fontSize").val());
            $("#filecon1").css("font-size",$("#fontSize").val()+"px");
            $("#filecon2").css("font-size",$("#fontSize").val()+"px");
            // 
            var start = new Date().getTime(); // 开始时间            shaixuan();
            shaixuan();
            var end = new Date().getTime(); // 结束时间           alert(timeUse);
            layer.msg("执行花费了:"+(end - start)/1000+"秒!");
           
    })
    function shaixuan(){
        layui.use('layer', function(){
            var  layer = layui.layer;
            layer.load(2);
        // layer.msg('请先选择好文件!');
        });  

        /**
         * 原理:
         * 先从a中拿出20个字,前两字 ,(中间16字),后两字
         * 然后去匹配b中的这个范围
         * 然后在这个范围中,匹配不同的地方。
         * 万一b没有开头或者尾巴呢?
         * 此时,a的索引从0 变为 1 ,尾巴在去匹配。 
         * */
        //获取内容
        var contentA = $("#filecon1").html();
        var contentB = $("#filecon2").html();
        //获取颜色
        var stringBgColor = $("#bgcolor").html();
        //更新好
        $("#name1").html($("#name1").html()+ "["+contentA.length+"字]");
        $("#name2").html($("#name2").html()+ "["+contentB.length+"字]");
        //待装配的html
        var contentB_wait = "";
        var contentA_wait = "";
        var num = 0;
        //间隔次数长度
        var position_MAX_done_split = 600
        //开头
        var positionTou = 0;
        //精确度
        var calc_jqd = 10;
        //精确
        var boundOut = 2;
        //开头+尾巴+中间长度
        // var positionCril = (contentA.length<contentB.length?contentA.length:contentB.length)- 1 - calc_jqd;
        var positionCril = 500-calc_jqd;
        //精确度- 范围
        var range =  15;
        //精确度。。截距
        var percent = 0.7;
        // var 
        //记忆,上次B截取到哪了
        //这里存储待比较的b的内容。也就是头找到了,尾巴也找到了。 一定要纪录此时的history
        var historyT = 0,historyW = 0,i,j,k;
        //这里需要改成while循环的,以便控制进度

        //判断长度
         //判断 是否大于20字节
         if(contentA.length < positionCril || contentB.length < positionCril){
             layui.use('layer', function(){
                var  layer = layui.layer;
                layer.closeAll();
            // layer.msg('请先选择好文件!');
            });  
            layer.msg("您的文件长度不够,配置的参数跟文本长度不符合要求!文件a和b的内容长度为"+contentA.length+","+contentB.length+",需要的尾巴长度为:"+positionCril);
             return ;
         }

         layer.msg("开始")
         var count = 0;
         control_lock = 1;
         //给你一次 清理最后尾巴的机会
         var giveYouTurnHasUse = 0;
         while(true){
            //加退出判断
            if(positionCril > contentB.length || count >= 10 || positionTou > contentA.length){
                    break;
            }
            //先从A中拿出字符串[0,20],精确度为2
            var currA = contentA.substr(positionTou,positionCril - positionTou);
            var currStart = contentA.substr(positionTou,calc_jqd);
            var currEnd   = contentA.substr(positionCril-calc_jqd,calc_jqd);
            // alert(currEnd+"  <这个是 尾巴 位置是 "+positionCril);
            //临时组装的
            var currB = "";
            // alert(currA + "             ||||||本次 头:"+currStart+"     本次尾巴: "+currEnd);
            
            for(j = historyT;j<contentB.length;j+=calc_jqd){
                //先找到B的头,就是上面A的头
                if(contentB.substr(j,calc_jqd) == currStart){
                    //说明找到头了。。要纪录history
                    historyT = j;                    
                    //然后要找尾巴。。。 +  精确度  从刚找到的开头后 + 精确度个字符开开始找..
                    for(k = historyT;k<contentB.length;k++){

                        // console.log("比较:"+contentB.substr(k,calc_jqd)+"   和  "+currEnd);
                        if(contentB.substr(k,calc_jqd) == currEnd && (k+calc_jqd-j) > (positionCril - positionTou)*percent){
                            // alert("wotm找到尾巴了!e")
                            //找到尾巴了! 此时应该截取比较的字符串。以及当前b的索引和a的索引。以便下次使用 而且当前长度不能小于 currA的长度
                            currB = contentB.substr(j,k+calc_jqd-j);
                            // console.log(currB+"   -----------------;"+contentB.substr(j,k+2*calc_jqd-j))
                            //其实好像不用记录尾巴。。
                            historyW = k+calc_jqd;
                            break;
                        }
                    }
                    //已经找到头部了,应该beak;
                    break;
                }

            }
            // alert("????????   ["+currA+"]     开头: [     {"+currStart+"}        ]结尾[          {"+currEnd+"}       ] ,需要比对的b[             {"+currB+"}          ]");
            //如果说,j的值大于文本长度,也就是说,没有找到开头。
            if(j > contentB.length - 1){
                //判断 头部 + 精确度 是否超过 尾巴 ,如果是,那么进行下一轮。
                //此时应该变换开头。重新找。。
                // console.log("为什么头都没有找到?"+positionTou);
                positionTou = positionTou + boundOut;
                continue;
            }
            //万一没有找到尾巴?这么办,那么此时应该改变尾巴,变为 + 2
            if(currB == ""){
                //尾巴长度加二
                // console.log("为什么尾巴都没有找到?"+positionCril);
                positionCril = positionCril + boundOut;
                continue;
            }
            // alert(currA+  "             ?????????          "+currB);
            // alert("本次  A:"+currA+"     ; 本次,B"+currB);
            //如果成功,那么进行进一步的筛选。。然后组装b,添加到 wait中 当然是遍历b的每个字符串,看看a中有没有  一切是a源文件比较
            //注意。  比较的是 a的当前截取的  和 b的当前截取的
            var currB_copy = "";
            //定义一个回溯变量
            var cancelVaire = 0;
            var sign = [".","。","`","~","·",",",",","!","@","#","$","%","^","&","*","(",")","_","+","=","{","}","[","]",";","*","-","<",">","/","\""];
            for(var r = 0;r < currB.length;r++){
                var is_has_element = 0;
                // alert("?currA = "+currA);
                if(sign.indexOf(currB.charAt(r))!= -1){
                    currB_copy = currB_copy + currB.charAt(r);
                    continue;
                }
                for(var q = cancelVaire ; q < currA.length; q ++){
                    //从B开始 因为头部和尾部 b都有了,所以不需要搜索
                    // alert("右边的: "+currB.charAt(r)+"    左边的:"+currA.charAt(q)+ "   上次回溯的是啥  "+"q - cancelVaire : "+q+"  "+cancelVaire+"  = "+(q - cancelVaire));
                    if(currB.charAt(r) == currA.charAt(q) && q - cancelVaire < range){
                        //不需要动手脚
                        // alert("竟然是相等的///");
                        currB_copy = currB_copy + currB.charAt(r);
                        is_has_element = 1; 
                        cancelVaire = q;
                        break;
                    }
                }
                if(is_has_element == 0){
                    //说明没有,需要加粗
                    currB_copy = currB_copy +'<a id="num'+num+'" style="background-color:'+stringBgColor+';color:rgba(255,255,255,1);">'+ currB.charAt(r)+'</a>';
                }
                // alert("这次的:               "+currB_copy);
            }
            //添加
            contentB_wait = contentB_wait + currB_copy+"<br><a style='color:red;'>[循环分割线]</a><br>";
            //为了那个列表展示定位
            num++;
            //加
            positionTou  = positionCril;
            positionCril += position_MAX_done_split;
            //测试,只执行前20代码
            // count++;
            //头,
            historyT = historyW;
            if(positionCril > contentA.length && giveYouTurnHasUse == 0){
                //如果
                positionCril = contentA.length-calc_jqd;
                giveYouTurnHasUse = 1;
            }
        }

        //执行好后,设置html
        // alert(contentB_wait);
        document.getElementById("filecon2").innerHTML = contentB_wait;
        //判断解析后,
        $("#name2").html($("#name2").html()+ "   解析后: "+$("#filecon2").text().length+"字]");
        layui.use('layer', function(){
            var  layer = layui.layer;
            layer.closeAll();
        });  
    }
        
</script>

    BUG肯定是有很多的。毕竟只用了半天时间来粗略写好,但基本的识别率已经达到了85%,匹配准确率达到了90%。时间复杂度也略微优化了一些。如果有时间,后续会完善逻辑和算法。每次新的一轮循环后,感觉contentB_wait头部会少那么几个文字,大概知道在哪里出问题。但没去解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值