文件比对是一个非常古老的话题,但是真要做好并不容易。
来看看下面两个文件:
FILE1:
A
B
D
E
F
FILE2:
B
C
D
E
G
首先琢磨一下,影响效率的因素:
1. 读文件,硬盘IO读写肯定是很花时间的,两种读法,一种是一次全部读入,比较占内存,另一种是边做边读,省内存,写起来麻烦一些,花时间;
2. 字符串比对,尤其长字符串,比对次数当然少一些最好;
3. 数据结构的选定
分析:
1. 我的程序文件没有超过100m的,可以一次性读入数组来操作,内存也还能吃得消;
2. 字符串还比较长,对于比对次数要小心谨慎,别瞎浪费时间;
算法一:
1. 读出两个文件的内容,放进两个listA和listB;
2. 二次循环比对,相等的元素放入listC同时删除掉listA和listB中的该元素:listC.add(item1);listA.delete(item1);listB.delete(item1)
3. 最后得到的结果就是A=B:listC;A-B=listA;B-A=listB,这里A-B的意思就是A文件中有但是B文件中找不到的元素
琢磨:首先list的效率本身就不怎么地,其次还频繁的delete和add,效率一般,不过算法够简单。
算法二:
1. 读出两个文件的内容,放进两个arrA和arrB;
2. 二次循环比对,相等的元素放入arrC,每次增加前都redim一次arrC,同时删除掉arrA和arrB中的该元素,又不是list,怎么删除呀,呃。。。那就把那个元素值改成某个确定不会出现在元素集合中的某个值。这招够馊的!不过不用重新整理数组,应该速度会快点
3. 最后得到的结果就是A=B:arrC;A-B=arrA去掉特定修改元素;B-A=arrB去掉特定修改元素,这里A-B的意思就是A文件中有但是B文件中找不到的元素
评价:
1.数组操作会快些,缺点是每次都redim重新分配内存,过于频繁,影响效率;
2.不过没重新整理数组这个不错;
3.比对了很多已经排除的元素,浪费了
看完了上面两个算法,都有比较明显的问题,能不能别重复比对元素,并且别在比对过程中不断重新分配内存的方法,大家来看看下面这个算法:
1. 读出两个文件的内容,放进两个arrA和arrB;
2. 二次循环开始比对,遇到相等的元素,arrA(i)和arrA(arrA起始下标+匹配上的元素-1)交换,arrB(j)和arrB(arrA起始下标+匹配上的元素-1)交换,这样不断的冒泡,arrA和arrB的前面就是匹配上的元素,后面就是各自独有的元素;
3. 最后得到的结果就是A=B:arrA(0, 匹配上的元素-1)=arrB(0,匹配上的元素-1);A-B=arrA(匹配上的元素,arrA的整个个数);B-A= arrB(匹配上的元素,arrB的整个个数),这里A-B的意思就是A文件中有但是B文件中找不到的元素
这个可能大家看得有点点晕忽,画个图吧:
大家看明白了吧,这个算法的优点是:
1. 不用比对过程中使用新的数据结构来不断分配内存空间;
2. 每次比对都是过滤后的元素比对,比对次数从笛卡儿乘积变成了阶乘;
3. 如果两个文件经过排序的,基本上就是arrA和arrB中用来做主比对数组的元素个数了,说到这里,大家一琢磨,就会发现,如果arrA和arrB大小不同,元素少的那个数组放第一层循环,会少循环几次,次数就是arrA和arrB元素的差值。
好吧,附带上代码给大家做个参考:
'比对两个文本文件,返回是否相等
'比对结果写入日志
'注意:比对的文件最后不要有空行
Function FblnCmpFile(strFile1,strFile2,strLog)
dim intlb1,intlb2,intub1,intub2,intfind
dim blnFind
dim strS1,strRes
dim sFileCont
dim i,j
'1.将strFile1和strFile2分别读入arr1和arr2
if not FblnFileExist(strFile1) then
Log.Warning(strFile1 & " 不存在")
FblnCmpFile=False
exit Function
End If
if not FblnFileExist(strFile2) then
Log.Warning(strFile2 & " 不存在")
FblnCmpFile=False
exit Function
End If
sFileCont=aqFile.ReadWholeTextFile(strFile1,aqFile.ctUnicode)
arr1=split(sFileCont,vbcrlf)
'Log.Message("文件1:" & sFileCont)
'for i=0 to ubound(arr1)
' Log.Message(arr1(i))
'Next
sFileCont=aqFile.ReadWholeTextFile(strFile2,aqFile.ctUnicode)
arr2=split(sFileCont,vbcrlf)
'Log.Message("文件2:" & sFileCont)
'for i=0 to ubound(arr2)
' Log.Message(arr2(i))
'Next
'2. 对两个数组进行比对
intlb1=lbound(arr1)
intlb2=lbound(arr2)
intub1=ubound(arr1)
intub2=ubound(arr2)
intfind=0
for i=intlb1 to intub1
blnFind=false
for j=intlb2+intfind to intub2
Log.Message(arr1(i) & vbcrlf & arr2(j))
if arr1(i)=arr2(j) then
intfind=intfind+1
Log.Message("swtich arr2:" & arr2(intlb2+intfind-1) & "和" & arr2(j))
strS1=arr2(intlb2+intfind-1)
arr2(intlb2+intfind-1)=arr2(j)
arr2(j)=strS1
blnFind=true
exit for
end if
next
if blnFind=true then
Log.Message("swtich arr1:" & arr1(intlb1+intfind-1) & "和" & arr1(i))
strS1=arr1(intlb1+intfind-1)
arr1(intlb1+intfind-1)=arr1(i)
arr1(i)=strS1
blnFind=false
end if
next
Log.Message("处理完成")
for i=0 to intub1
Log.Message(arr1(i))
Next
for i=0 to intub2
Log.Message(arr2(i))
Next
'3.写日志
'A=B:arr1(intub1-intfind,intub1)
'A-B:arr1(0,intub1-intfind-1)
'B-A:arr2(0,intub2-intfind-1)
sFileCont="比对文件: " & vbcrlf & "[" & strFile1 & "] 和 [" & strFile2 & "]" & vbcrlf & vbcrlf
sFileCont=sFileCont & "比对结果为:" & vbcrlf
sFileCont=sFileCont & "A 共有记录: " & intub1+1 & "条" & vbcrlf
sFileCont=sFileCont & "B 共有记录: " & intub2+1 & "条" & vbcrlf
sFileCont=sFileCont & "找到AB相同记录: " & intfind & "条" & vbcrlf
if intfind=intub1+1 and intfind=intub2+1 Then
sFileCont=sFileCont & vbcrlf & "两个文件完全相等" & vbcrlf
FblnCmpFile=True
Else
sFileCont=sFileCont & vbcrlf & "两个文件不相等" & vbcrlf
'A=B
strRes=""
for i=intlb1 to intlb1+intFind-1
strRes=strRes & arr1(i) & vbCrLf
next
sFileCont=sFileCont & vbcrlf & "A=B: " & vbcrlf & strRes & vbcrlf
'A-B
strRes=""
for i=intlb1+intFind to intub1
strRes=strRes & arr1(i)& vbCrLf
next
sFileCont=sFileCont & vbcrlf & "A-B: " & vbcrlf & strRes & vbcrlf
'B-A
strRes=""
for i=intlb2+intFind to intub2
strRes=strRes & arr2(i)& vbCrLf
next
sFileCont=sFileCont & vbcrlf & "B-A: " & vbcrlf & strRes & vbcrlf
FblnCmpFile=False
end if
call aqFile.WriteToTextFile(strLog,sFileCont,aqFile.ctUnicode,True)
End Function
使用方法:
strFile1=Project.Path & "data\file1.tmp.txt"
strFile2=Project.Path & "data\file2.tmp.txt"
strLog=Project.Path & "data\fcmp.log"
call FblnCmpFile(strFile1,strFile2,strLog)
God bless u!