作者在 2026-08-04 16:12:26 发布以下内容
文本对照后,可以创建一个“目录”,用以快速定位“改动过的区域”。
在文本行数比较多、编辑点又比较分散时,如果要反复观察“不同的对照区域”的话,则需要反复拖动滚动条的滑块,同时还需留意滚动中的文本,手忙眼花。程序要是能象“章回小说”那样,提供一个“目录”。用户只要在“关注的目录条”中任意选中几个字,两个相关区域就会立刻同步跳转到“窗口”(顶部)。那样就方便多了。
操作“目录”,就是把2个文本中有特别意义的行,滚动到窗口的第1行(后面简称“顶行”),方便用户观察、分析。
在文本行数比较多、编辑点又比较分散时,如果要反复观察“不同的对照区域”的话,则需要反复拖动滚动条的滑块,同时还需留意滚动中的文本,手忙眼花。程序要是能象“章回小说”那样,提供一个“目录”。用户只要在“关注的目录条”中任意选中几个字,两个相关区域就会立刻同步跳转到“窗口”(顶部)。那样就方便多了。
操作“目录”,就是把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 操作“垂直滚动条”时的同步滚动。
下面是相关的代码:
Private Sub DiSan() '建立目录文本 (参考插图。)
一、 当前在窗口顶行的是文本内容的第几行 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

