作者在 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的更会“云里雾里”。
批改就是把“打字文本”和“样张文本”进行比对。
批改的最起码的要求,就是在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的更会“云里雾里”。
文中若有错误或不妥之处,欢迎指出。希望能与朋友商榷和交流。如有“没有说清楚”的地方,可向我提出疑问,我会尽力回复。