文本对照后的“编辑位置目录”

文本对照后,可以创建一个“目录”,用以快速定位“改动过的区域”。
在文本行数比较多、编辑点又比较分散时,如果要反复观察“不同的对照区域”的话,则需要反复拖动滚动条的滑块,同时还需留意滚动中的文本,手忙眼花。程序要是能象“章回小说”那样,提供一个“目录”。用户只要在“关注的目录条”中任意选中几个字,两个相关区域就会立刻同步跳转到“窗口”(顶部)。那样就方便多了。
操作“目录”,就是把2个文本中有特别意义的行,滚动到窗口的第1行(后面简称“顶行”),方便用户观察、分析。

怎样创建“目录”?先介绍几个要用的“工具”:
一、 当前在窗口顶行的是文本内容的第几行 lngVPos:

lngVPos = SendMessage(RT1.hwnd, EM_GETFIRSTVISIBLELINE, 0, ByVal 0&) 其中 RT1 是文本框。

二、 指定位置(s)的字,在文本内容里是第几行(NewLine)

例如:文本 Text1(200个字)中有1个词“亮眼”(全文唯一),要知道它是在文本第几行(跟它现在是否看得见无关),先要查明它的“位置”是第几个字(s)

s=InStr(1,Txt1,"亮眼") ★在博文后面“操作目录的代码”里,“亮眼”对应于变量 hh。

然后算出“亮眼”在第几行NewLine:

NewLine = RT1.GetLineFromChar(s)

三、当需要把含这个“亮眼”词的行滚动到窗口顶行时,文本应该要滚动的“行数”lngVDiff:

lngVDiff = NewLine - lngVPos

四、让文本在文本框RT1里滚动 lngVDiff 行的滚动命令:

SendMessage RT1.hwnd, EM_LINESCROLL, 0, ByVal lngVDiff (最后实现目标)

说明:
一、关于 SendMessage 、GetLineFromChar(s) 和 InStr(1,Txt1,"亮眼") 这三个“命令”,请自己查看相关的资料,并注意2个“常数”: EM_GETFIRSTVISIBLELINE 和 EM_LINESCROLL 的意义。

二、本文中所有的“第几行”是从文本内容在控件里(不是窗口)的首行算起见。文本无论怎样滚动,各行的内容都是不变的。例如:如果“亮”字是在第3行第12个,那么,无论文本怎样滚动,它总在第3行第12个,即使它暂时看不见。

我们的目标是:把各次“开始编辑”的行(即由“左右相同”转变为“左右不同”的行)滚动到“可见第一行”或者第二行。第二行更好,可以让用户确信这是差异的开始(见图)。

看来,问题的关键是:每次滚动需要确定1个“在【需要滚动到窗口顶部的行】中存在、并且在全文中独有的”字符串。★“唯一的关键字符串”

我在“CSDN网站”和“BCCN网站”发表过题为《文本比对程序设计--按‘求同存异’来梳理文本;用‘反转的文本为‘状态数组’充值》的文章。里面介绍了可以获取“每一次编辑的起始行号QS()和连续行数CD()”的方法。在这里可以用到的是“起始行号QS()”。(当然,左端显示“ 85 ”的行,在文本里的行数肯定超过 85。)

利用这两组数据,就可以建立“目录”和操作“目录”了
文末附上的3个过程的代码很短(我实际使用的),可以供读者参考。

要编制这个程序,还有几个必须的环节:
1 最重要的“比对算法”,在本文中提到的、在本网站发表过的博文中有详细的叙述。
2 利用上一环节中得到的数据QS()和CD(),给相应的行加上“+”、“-”、“!”标志。插入“///////”行。
3 利用行首的“+”、“-”、“!”标志,以及文本中的 " Added "等标志给相关的行设置彩色高亮。
4 操作“垂直滚动条”时的同步滚动。

如果要运行我上传的“带目录的比对程序”,需要在系统中注册2个 OCX 文件:
COMDLG32.OCX 和 RICHTX32.OCX

Private Sub DiSan() '建立目录文本 (参考插图。) Dim cha$, sha$, cuo$, mn%, Txt2new$, k$, sp$, xu$ sp = String(25, " ")'项目条之间的空行 cha = " Added "'新增 sha = " Deleted "'删除 cuo = " Changed "'更换 Txt2new = "编辑位置目录: " & vbCrLf '有5个空格,用来退出。 mn = 1'第mn次编辑 Do xu = Left$(mn & " ", 3) k = Left$(CD(0, mn) & "---" & CD(1, mn) & " ", 11) _ & "------ " & Left$(Trim$(Str$(QS(1, mn) - 1)) & " ", 3) & vbCrLf & sp ' ★“QS(1, mn)” 表示编辑后文本的第mn次编辑的起始行的行号 ,“-1”表示“上一行” ' ★ 关键数据是每个目录条的“最后5个字符” If CD(0, mn) > 0 And CD(1, mn) > 0 Then' Txt2new = Txt2new & xu & cuo & k & vbCrLf'更换 ElseIf CD(1, mn) = 0 Then' Txt2new = Txt2new & xu & sha & k & vbCrLf'删除 Else Txt2new = Txt2new & xu & cha & k & vbCrLf'新增 End If mn = mn + 1 Loop Until mn > mnMAX RT3.Text = Txt2new' RT3 是目录文本框 End Sub '操作目录 Private Sub RT3_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single) Dim txt3$, hh$, a&, s2&, s3& If RT3.SelLength = 0 Then Exit Sub a = RT3.SelStart + 1 txt3 = RT3.Text '找“行尾”的“回车”位置 s3 = InStr(a, txt3, vbCrLf) ' 如果行尾是5个空格,就退出。 If Mid$(txt3, s3 - 5, 5) = " " Then Exit Sub '★标题和空白行的最后5个字都是空格 hh = vbCrLf & Mid$(txt3, s3 - 5, 5) '★“回车”加上目录条行尾的5个字符,就成了“编辑后文本RT2”的唯一关键词:行号。 txt2 = RT2.Text s2 = InStr(1, txt2, hh) + 2' 在编辑后文本RT2里寻找“唯一的关键词” NewLine = RT2.GetLineFromChar(s2)' NewLine是公共变量,需要滚动到窗口顶行的内部行号。 Tong12' 调用“滚动”过程,送去 NewLine 的值。实现滚动。 DoEvents End Sub Public Sub Tong12() '被“操作目录”过程调用 '★NewLine是公共变量,由调用Tong12的过程送来的、 '“要滚动到窗口顶部的行”的内部行号 'RT1是编辑前文本框,RT2是编辑后文本框 lngVPos1 = SendMessage(RT1.hwnd, EM_GETFIRSTVISIBLELINE, 0, ByVal 0&) DoEvents lngVDiff = NewLine - lngVPos1 If lngVDiff <> 0 Then SendMessage RT1.hwnd, EM_LINESCROLL, 0, ByVal lngVDiff End If DoEvents lngVPos2 = SendMessage(RT2.hwnd, EM_GETFIRSTVISIBLELINE, 0, ByVal 0&) DoEvents lngVDiff = NewLine - lngVPos2 If lngVDiff <> 0 Then SendMessage RT2.hwnd, EM_LINESCROLL, 0, ByVal lngVDiff End If DoEvents End Sub

MDLG32.OCX 和 RICHTX32.OCX