文本比对在中文打字评分中的运用--正确、错、漏、多的发现与应用

作者在 2021-06-18 10:03:44 发布以下内容
      第一部分  发现错、漏、多
    批改就是把“打字文本”和“样张文本”进行比对。
    批改的最起码的要求,就是在2个文本中,每个字都须定性为“正确”或“不正确”(样张中的“不正确”,意思是指“没有被正确地打出来,”,就是“被漏打”和“被打错”)。(例3中的绿色“带儿子”),(例4中的红色“莫斯科去”);在录入中,不正确的字也可以分为2类:,一类叫做“多打”(例2中的蓝色“的”),另一类叫做“打错”(例4中的红色“墨西哥”)
“正确”的3个原则是:1.两个文本中的正确字一一对应。 2.不能颠倒顺序。3.选“给分最多”的方案。
例1:样张是“他叫英白罗”,打字是“他叫罗英白”。正确(给分)字是4个:“他叫英白”。“罗”不给分,正是符合了这3个原则。
    这样一来,就必须注意:为了符合第1、2原则,两个文本中已经“定性”的字符,不可以再参与比对。其左边的字,也不可再参与比对。
    文本比对的基本操作,其实是单个文字(字符)的比对,而这种比对,只能判断两个字“是否相同”(并非“是否正确”,见“罗”字)。怎样才能批改出“正确”与“不正确”,甚至“对、错、漏、多”呢?我觉得,按怎样的规则来取出比对的“字”(出字顺序),是关键。
    我的方法是:把“录入文本”中的每一字(zn2) ,按从先到后(就是从左到右)的顺序,拿出来与样张中的字(zn1)比对。必须等到zn2定性后,才可以取出下一个来(n2+1,其中n2表示zn2的位置)。
    那么,样张中的zn1是按什么顺序来取出的呢?
    zn1 是样张里“还没有定性的、最左面的字”。或者说,zn1左面的字都是已经定性了的字;zn1及其的右面都是还没有定性的字。
    比对的语句,在不同的编程语言里会不同,但意思都是:如果 zh1 与 zn2 相同,那么怎样怎样;否则怎样怎样。    (不同的编程语言中,“从字串中取出字符”的方法各不相同,在VB里有现成的函数。)
    zn1与zn2比对时,会有以下四种情况:(①、②、③1、③2)
    如果zn1与zn2相同,那么,2个都是正确字。这种情况是大多数,除非打得“一塌糊涂”。我称之为“第一种情况”,我会在插图中zh2的下面用①标出来。
    如果zn1与zn2不相同,那么,接着把zn2与“zn1右面的字”顺序、逐个地进行比对,这过程中,又有2种情况,一种是:直至样张结束,都没遇到与zn2相同的字,这时,zn2肯定是不正确的字,而zn1不能肯定。我用②来标志。这种情况是少数。典型的例子就是“全中文的样张却打了一个拼音或英文字母”。
★而比较复杂的是,在zn1右面(移过了若干个字,我用K来计数)找到了与zn2相同的字我称为“第3种情况”(比如例1“罗”,还有例3、例4) 。
那么,例3中的“到”(和例4中的“旅”)是否是正确的字呢,这里正是需要依据“正确字”的第3个原则(“给分最多”),这就需要进一步的“计算”,在这里由于比较简单,可以知道“到”、“旅”是正确字(样张里的字“到”、“旅”我标为z3),我用“③1”标志这个结果。而在“例1”中,“罗”却不是正确字我标为③2。
注意★:如果是“第3种情况”,就必须要进一步计算究竟是③1还是③2,这正是比对中的难点。(在我以前的博文中,有比较详细的叙述,)由于这涉及到“最长公共子序列”、“动态规划”、“递归函数”等等问题,叙述起来有很大的篇幅。这里不再赘述。
★这里特别要指出的是,根据原则2.不能颠倒顺序。 ,例3样张中的“带孙子”就定性为“不正确”了,我用K来记录其字数,例3中K=3,例4中K=4。
如果经过进一步的计算,样张中找到的这个字并不是zn2的对应字(例1的样张中的“罗”),那么就把这个字【当作是“与zn2不同的字”】(不能称为z3),继续往样张的后面找,找到,就再计算……。如果直到样张的最后都不能得到③1的结论,那么zn2就定性为“不正确”,zn1不能肯定,就跟②一样,我标志为③2。“③2”与②的效果★完全一样。
    至此,我们可以归纳一下“正确字”、“不正确字”的“来历”:
    “正确字”----------①、③1。 
    “不正确字”----:
    打字文本那里“不正确字”--------由②、③2情况产生,连续的不正确字数可以通过一个一个累加而得到(我用Tmp来表示,见流程图)。
    这些字(Tmp)有2个转变,在例1、例2中转为“多(蓝色)”;在例4中转为“错2(红色)”;
    样张那里“不正确字”--------由③1情况产生,(例3样张中的“带孙子”)每次连续的部分用K记录,见“第3种情况”的例3的说明。
    这些字(K)也有2个转变,在例3中转为“漏(绿色)“带孙子””;在例4中转为“错1(红色)”(“莫斯科去”)。
    那么,“不正确字”又是怎么转变为“多、漏、错”的呢?转变是在“差异结束”的时候发生的。我们借助于彩色图 例1-例4,可以得出规律来: 
    多----见图例1(只在录入文本): 遇到①时, 如果有 Tmp>0。打字文本的“差异结束”,(Tmp>0,K=0)就是(纯多)。请看流程图中的“差异结束a”。
    漏----见图例3(只在样张文本): 遇到③1时,如果有 Tmp=0。只有样张“差异产生又结束”, (Tmp=0,K>0) 就是(纯漏)。请看流程图中的“差异结束b”。
    错----见图例4(两边文本都有): 遇到③1时,如果有 Tmp>0。两个文本都发生“差异结束”,(Tmp>0,K>0),习惯就说是错(又多又漏就是错。把“鳌”错打成“鳖”,也可以说“漏打了‘鳌’”、“多打了‘鳖’”)。请看流程图中的“差异结束b”。
    我们再梳理一下两个文本的“出字顺序”:(用n1、n2来表示zn1和zn2的位置)
对于打字文本,我们已经说过了:定性一个,取出下一个,都是 n2+1。(看流程图)
而对于样张,要看3种情况:(看流程图)
情况① ----zn1、zn2 都正确,下一个字是:n1+1; n2+1
情况②或③2----zn2“不正确”,n2+1。 而zn1不能肯定,不取出下一个字: n1不动,不做 n1+1。
情况③1----zn2、z3正确,zn1和它到z3之前共有K个字都不正确。下一个字是: ★n1+K+1 ,n2+1。(漏打、打错,正是由情况③1而来)
   要是你能把各个例子中的两行文本,放到流程图里走一遍的话,你就会对此主题有更好的理解。而且对于我第二部分中的“左右正误对照”,阅读起来会更容易一些。

      第二部分  应用----分类统计、标注颜色和左右正误对照
   分类统计由于比较简单,就不说了。
   我们先看一下程序批改后的界面。一是“错、漏、多”字符标上了不同的颜色,二是★“左右正误对照”的那个“下拉列表”。当选择某一项时,会自动找到相应的字并★选中。就可以很“醒目”地左右对照了。
   这2件事(标上颜色和左右选中对照)都需要2个数据:1.发生差异的起始位置QS(长整型SelStart,例子中的 zn1和zn2位置),2.不正确字串(差异)的长CD(SelLength,例子中的连续的红、绿、蓝色字符串的长度),就是前面提到的Tmp、K(整型)。因为在不同的文本(样张、录入)和不同的差异点,这2个数据都不同,所以2个都是二维的数组。
   数组QS()  和  CD()   (中文“起始”和“长度”的拼音首字母)
   第一维的值只有0和1,分别表示“样张”和“打字”。第二维是发生差异的次数mn,由于不能预先确定,所以采用“动态数组”,并将“差异次数mn”放在最后一维(第2维)。
  1.起始点(QS):请看流程图中的“差异开始”位置,就是“zn1 与 zn2 不同,而且之前‘Tmp=0’ ”,(由‘正确’转为‘不同’)的时候。
VB 代码:
        If zn1 <> zn2 Then  
            If Tmp = 0 Then'差异起点
                mn = mn + 1'    新的差异点。增加(mn)发生差异的次数
                ReDim Preserve QS(1, mn)'  重定义,扩大起始点最后一维mn的上限,并且保留数据。
                ReDim Preserve CD(1, mn)'   ..........长度,……
                QS(0, mn) = n1'  记录在样张的起始点n1
                QS(1, mn) = n2'  记录在打字的起始点n2
            End If
        End If
   2.差异长度(CD):在(两个)差异结束的位置(由‘差异’转‘正确’)就可以得到差异的长度(Tmp、K)。请看流程图中的两个位置:“差异结束a、差异结束b”。
   在位置a:情况①(这里是“正确”位置)
                If  Tmp > 0 Then'由不正确转为正确
                      CD(0, mn) = 0'  第一下标0表示记录在样张的差异长度是0。  因为这个地方只有“多”的差异。
                      CD(1, mn) = Tmp'  第一下标1表示记录在打字的差异长度是Tmp。
                      ComErr.AddItem "No." & Right("0" & Trim(Str(mn)), 2) & "  多" & Tmp ' & " 字"
                   '上面这句是在下拉列表ComErr中添加项:   “No.05  多  8  字”(假设这时mn=5,Tmp=8)
                      Tmp = 0
                End If
         ★左右2个文本文字的关联,就是“差异序号 mn”,下拉列表其实就是选某个序号。
   在位置b:情况③1(这里也是“不正确转为正确”位置)   前面说过,当 Tmp>0 而且 K>0 时,就是打错;当 Tmp=0  而且  K>0 时,就是漏打。
   因为b是在“情况③1的位置(看流程图),所有必定有 K>0。
    VB 代码:       CD(0, mn) = K ' 记录样张的差异长度K 
                            If   Tmp > 0 Then' 错
                                 CD(1, mn) = Tmp  '记下录入的差异长度
                    ComErr.AddItem "No." & Right("0" & Trim(Str(mn)), 2) & " 错" & K & "--" & Tmp ' & " 字"
                                 Tmp = 0
                                 K = 0
                            Else'  因为Tmp=0,就是漏
                                 CD(1, mn) = 0  '  记下录入的差异长度是0。
                    ComErr.AddItem "No." & Right("0" & Trim(Str(mn)), 2) & "  漏" & K '& " 字"
                            End If '
    ★通过上面的 1.和 2.,就把数据储存到2个数组 QS()、CD() 以及 下拉控件ComErr中保存起来了。
只要在下拉列表中选择某一项(mn=ComErr.ListIndex + 1),就会自动找到差异点位置,可以完成这个“差异点”的左右两边的选中、对照了。
    有了这2个数据,就可以在不正确的字符上标志颜色了。可以用循环的方法。循环次数就是最大差异数mnMAX。
    '下面的VB代码中,RT3是样张文本框, RT4是打字文本框。
Private Sub cmdCol() '用颜色标示差异。
    Dim i%
    For i = 1 To mnMAX
        RT3.SelStart = QS(0, i) - 1
        RT3.SelLength = CD(0, i)
        If CD(1, i) = 0 Then
            RT3.SelColor = RGB(0, 128, 0)'漏-绿
            RT3.SelUnderline = True
            RT3.SelBold = True
        Else
            RT3.SelColor = RGB(255, 0, 0)'错-红
            RT3.SelUnderline = False
            RT3.SelBold = False
        End If
    Next i
    ......
    For i = 1 To mnMAX
        RT4.SelStart = QS(1, i) - 1
        RT4.SelLength = CD(1, i)
        If CD(0, i) = 0 Then
            RT4.SelColor = RGB(0, 0, 255)'多-蓝
            RT4.SelUnderline = True
            RT4.SelBold = True
        Else
            RT4.SelColor = RGB(255, 0, 0)'错-红
            RT4.SelUnderline = False
            RT4.SelBold = False
        End If
    Next i
    RT3.SelStart = 0
    RT4.SelStart = 0
End Sub
    ★当在文本中选中某个彩色字符时,程序可以由其位置来算出其所处的差异序号,从而自动求出另一个文本框的对应差异位置,自动选中,实现“左右正误对照”。
(当然,为使两边都显示“选中”,那两个文本框的 HideSelection 属性都要设置为 False)。
(题外话:“错、漏、多”字符,其实就是“两个字符串的 LCS 问题”中的“应该剔除的字符”,也可以叫“不参与的字符”。)
    不好意思,博文中插入了一些 VB 代码。即使是自己写的代码,时间久了,都会忘记初衷。不熟悉VB的更会“云里雾里”。

    文中若有错误或不妥之处,欢迎指出。希望能与朋友商榷和交流。如有“没有说清楚”的地方,可向我提出疑问,我会尽力回复。

批改后.png (上传于2021-06-18 10:03:44)
批改后.png

例1-例4.png (上传于2021-06-18 10:03:44)
例1-例4.png

流程图.png (上传于2021-06-18 10:03:44)
流程图.png

默认分类 | 阅读 1802 次
文章评论,共4条
William1949
2021-07-30 10:20
1
博主:你好

   看了你的文章,产生了一些好奇心,有个疑问:你写的程序,它的执行速度有多快?

   不瞒你说,我之前也写了一个“文本比对”的程序,用两个超过3万字的文本字符串,进行对比。耗时超过9秒!慢...

   如果你对这方面的问题,感兴趣,可以交流呀!!!
nhjsjjs(作者)
2021-07-30 13:26
2
你好!
很高兴能与你进行交流。我认为“耗时”的多少跟两个文本的差异情况有比较大的关系。我以为差异“很小”或“很大”的“耗时”较少。我2020年5月上传过一个“文本修改前后对照”的程序。你把你所说的那两个文本放进去看看,把“耗时”与你的程比较一下。盼你把结果告诉我。盼能继续交流。
nhjsjjs(作者)
2021-07-30 13:43
3
William1949: 你好!
另有一个“‘非描红式’打字测试程序”,因为没有设置“防粘贴”,你也可以把那个文本放进去,批改试试,耗时多少。
William1949
2021-08-03 21:17
4
“... 我认为“耗时”的多少跟两个文本的差异情况有比较大的关系。我以为差异“很小”或“很大”的“耗时”较少。 ...”


这就是我好奇之处。不了解你的算法。(代码的运行过程);没想通、哈哈;

实测,你较快。但是没法验证对比结果,因为,当两文本超过 32000 时,提示“溢出”;
当两文本小于 10000 时,在下方窗口选中 ● 时,提示“越界”;
并且,在下方窗口的最后一行选中“保留的内容”(某个正常的字符),不能与上方两个窗口中的字符一一对应。应该算是 BUG 吧。所以我不确定,你的结果跟我的结果是否有相同性。貌似,你把很大一段,都定义为“新增加的内容”;

好啦,这到这里吧,交流愉快!!!祝快乐!!!
游客请输入验证码
浏览91544次
文章分类