line input 太慢。实际上我的行数超过65535,是一个long, line input慢慢循环下去实在太慢,这样最后面的行我基本上没法看了
你可以考虑这样一个办法: VB里提供了一个Seek函数和Seek语句。Seek函数是返回当前文件指针的字节位置,而Seek语句是设置文件指针的位置。 只要你知道第3000行的字节位置,只要一个Seek语句就可以定义到第3000行。但是如何知道第3000行的位置这有点困难。一般的做法是:给你的文件制作一个索引文件,这个索引文件记录着行与字节的对应关系。或者也可以用大数组来实现。不过在更新频繁的情况下就成了问题,生成索引数据需要一定时间。下面是我临时写的一个代码,仅仅说明原理,但是不一定可以运行:Dim tSeekLocates() As LongOpen FileName For Input As #1 Do tStartLine=tStartLine+1 '行号记数 ReDim Preserve tSeekLocates(tStartLine) '伸长数组 tSeekLocates(tStartLine)=Seek(1) '记录与行号对应的字节数 Line Input tReadStr Loop Until EOF(1) CLose #1根据上面得到的数组tSeekLocates(),假如你想定位到第3000行,只要Seek #1,tSeekLocates(3000)就可以了。
以上的办法适合只读不写的场合,只要文件不改变,那么索引数据始终有效。你可以用二进制文件保存该数组。tSeekLocates(0)=UBound(tSeekLocates) '由于不存在第0行,所以用第0行保存数组的长度。Open SeekFileName For Binary As #1 Put #1,1,tSeekLocates() '写数组到文件。 Close #1读数组的时候需要先知道数组的长度,也就是第0号元素的值。 Open SeekFileName For Binary As #1 Dim tBound As Long Get #1,1,tBound '从第一个字节获得数组0号元素的值,也就是数组的长度。 ReDim tSeekLocates(tBound) '定义数组长度。 Get #1,1,tSeekLocates() '读数组到文件。 Close #1以上仅仅是简单的原理,实际上仔细写起来应当用Type定义你自己的结构实现。上面的程序不一定能运行,可能有的地方有些错误,仅仅说明原理。
将文件读入,再写入随机方式文件。。如果以随机方式打开文件,Seek返回下一个记录的编号。DimMyRecordAsRecord '声明变量。 Open "TESTFILE" For Random As #1 Len=Len(MyRecord) Do While Not EOF(1) '循环至文件尾。 Get#1,,MyRecord '读入下一个记录。 Debug.PrintSeek(1) '在立即窗口中显示记录号。 LoopClose#1 '关闭文件。如果不以Random方式打开文件,则Seek返回下一个操作会发生的位置。 假设TESTFILE文件内含有文本数据。 Dim My Char Open"TESTFILE" For Input As #1 '打开输入文件。Do While Not EOF(1) '循环至文件尾。 MyChar=Input(1,#1) '读入下一个字符。 Debug.PrintSeek(1) '将下一字符的位置显示在立即窗口。 LoopClose #1 '关闭文件。
本示例使用Seek语句在文件内设置下一次读写的位置。 示例中假设TESTFILE文件内含有用户自定义数据类型Record的记录。 TypeRecord '定义用户自定义数据类型。 ID As Integer Name As String*20 EndType如果以随机方式打开文件,Seek将读写位置设置到下一个记录。 Dim MyRecord As Record,MaxSize,RecordNumber'声明变量。'以随机文件方式打开文件。 Open "TESTFILE" For Random As #1 Len=Len(MyRecord) MaxSize=LOF(1)\Len(MyRecord)'取得文件中的记录的数。'用循环读入所有记录,但是从最后的记录开始往前读。 For RecordNumber=MaxSize To 1 Step-1 Seek#1,RecordNumber '设置读写位置。 Get#1,,MyRecord '读入一个记录。 Next RecordNumber Close#1
初步给你写了一个函数,并且做了实际的测试,后面有测试结果。不妨尝试一下:Function IndexArrayGetBySourceFile(pFileName As String, Optional pEventON As Boolean = False, Optional pEventLineMax As Long = 100) As Long() 'IndexArrayGetBySourceFile函数。 '语法:long tOutIndexs()=IndexArrayGetBySourceFile(pFileName, [pEventON], [pEventLineMax]) As Long() '功能:为文本文件建立行号到地址的索引表。 '参数:string pFileName 文件名。必须指向一个合法文件。 ' boolean pEventON 可选参数。响应事件开关。默认为False ' long pEventLineMax 可选参数。响应事件的工作数。默认为100行。 '返回:long tOutIndexs() 整形数组。行数对应的数组元素保存有该行的字节地址。 Dim tOutLongs() As Long
Dim tFileNumber As Integer Dim tReadStr As String Dim tLineNumber As Long Dim tFileSize As Long Dim tWorkState As Long
tFileNumber = FreeFile tLineNumber = 0
Open pFileName For Input As #tFileNumber
tFileSize = LOF(tFileNumber)
Do
tLineNumber = tLineNumber + 1 Line Input #tFileNumber, tReadStr ReDim Preserve tOutLongs(1 To tLineNumber) tOutLongs(tLineNumber) = Seek(tFileNumber)
If Not CBool(tLineNumber Mod pEventLineMax) And pEventON Then 'tWorkState = tOutLongs(tLineNumber) * 100 \ tFileSize 'Form1.Text1 = tWorkState DoEvents End If
Loop Until EOF(tFileNumber)
Close #tFileNumber
IndexArrayGetBySourceFile = tOutLongs() End Function 测试代码:Private Sub Command1_Click() Dim tIndexDatas() As Long Dim tRndLineNum As Long Dim tRndLineAdd As Long
line input慢慢循环下去实在太慢,这样最后面的行我基本上没法看了
只要你知道第3000行的字节位置,只要一个Seek语句就可以定义到第3000行。但是如何知道第3000行的位置这有点困难。一般的做法是:给你的文件制作一个索引文件,这个索引文件记录着行与字节的对应关系。或者也可以用大数组来实现。不过在更新频繁的情况下就成了问题,生成索引数据需要一定时间。下面是我临时写的一个代码,仅仅说明原理,但是不一定可以运行:Dim tSeekLocates() As LongOpen FileName For Input As #1
Do
tStartLine=tStartLine+1 '行号记数
ReDim Preserve tSeekLocates(tStartLine) '伸长数组
tSeekLocates(tStartLine)=Seek(1) '记录与行号对应的字节数
Line Input tReadStr
Loop Until EOF(1)
CLose #1根据上面得到的数组tSeekLocates(),假如你想定位到第3000行,只要Seek #1,tSeekLocates(3000)就可以了。
Put #1,1,tSeekLocates() '写数组到文件。
Close #1读数组的时候需要先知道数组的长度,也就是第0号元素的值。
Open SeekFileName For Binary As #1
Dim tBound As Long
Get #1,1,tBound '从第一个字节获得数组0号元素的值,也就是数组的长度。
ReDim tSeekLocates(tBound) '定义数组长度。
Get #1,1,tSeekLocates() '读数组到文件。
Close #1以上仅仅是简单的原理,实际上仔细写起来应当用Type定义你自己的结构实现。上面的程序不一定能运行,可能有的地方有些错误,仅仅说明原理。
Open "TESTFILE" For Random As #1
Len=Len(MyRecord)
Do While Not EOF(1) '循环至文件尾。
Get#1,,MyRecord '读入下一个记录。
Debug.PrintSeek(1) '在立即窗口中显示记录号。
LoopClose#1 '关闭文件。如果不以Random方式打开文件,则Seek返回下一个操作会发生的位置。
假设TESTFILE文件内含有文本数据。
Dim My Char
Open"TESTFILE" For Input As #1
'打开输入文件。Do While Not EOF(1)
'循环至文件尾。
MyChar=Input(1,#1) '读入下一个字符。
Debug.PrintSeek(1) '将下一字符的位置显示在立即窗口。
LoopClose #1 '关闭文件。
示例中假设TESTFILE文件内含有用户自定义数据类型Record的记录。
TypeRecord '定义用户自定义数据类型。
ID As Integer
Name As String*20
EndType如果以随机方式打开文件,Seek将读写位置设置到下一个记录。
Dim MyRecord As Record,MaxSize,RecordNumber'声明变量。'以随机文件方式打开文件。
Open "TESTFILE" For Random As #1 Len=Len(MyRecord)
MaxSize=LOF(1)\Len(MyRecord)'取得文件中的记录的数。'用循环读入所有记录,但是从最后的记录开始往前读。
For RecordNumber=MaxSize To 1 Step-1
Seek#1,RecordNumber '设置读写位置。
Get#1,,MyRecord '读入一个记录。
Next RecordNumber
Close#1
1:我这个文件是个LOG文件,在不断不断增加
2:实际上这个文件现在已经有100M大小,要建立数组也会非常漫长的
我用过unix下的wc -l,能非常快的得出文件的行数,谁知道它的运算原理??
'IndexArrayGetBySourceFile函数。
'语法:long tOutIndexs()=IndexArrayGetBySourceFile(pFileName, [pEventON], [pEventLineMax]) As Long()
'功能:为文本文件建立行号到地址的索引表。
'参数:string pFileName 文件名。必须指向一个合法文件。
' boolean pEventON 可选参数。响应事件开关。默认为False
' long pEventLineMax 可选参数。响应事件的工作数。默认为100行。
'返回:long tOutIndexs() 整形数组。行数对应的数组元素保存有该行的字节地址。
Dim tOutLongs() As Long
Dim tFileNumber As Integer
Dim tReadStr As String
Dim tLineNumber As Long
Dim tFileSize As Long
Dim tWorkState As Long
tFileNumber = FreeFile
tLineNumber = 0
Open pFileName For Input As #tFileNumber
tFileSize = LOF(tFileNumber)
Do
tLineNumber = tLineNumber + 1
Line Input #tFileNumber, tReadStr
ReDim Preserve tOutLongs(1 To tLineNumber)
tOutLongs(tLineNumber) = Seek(tFileNumber)
If Not CBool(tLineNumber Mod pEventLineMax) And pEventON Then
'tWorkState = tOutLongs(tLineNumber) * 100 \ tFileSize
'Form1.Text1 = tWorkState
DoEvents
End If
Loop Until EOF(tFileNumber)
Close #tFileNumber
IndexArrayGetBySourceFile = tOutLongs()
End Function
测试代码:Private Sub Command1_Click()
Dim tIndexDatas() As Long
Dim tRndLineNum As Long
Dim tRndLineAdd As Long
OnTimer = Timer
tIndexDatas() = IndexArrayGetBySourceFile("ALL.TXT")
tRndLineNum = Int(Rnd * UBound(tIndexDatas)) + 1
tRndLineAdd = tIndexDatas(tRndLineNum)
Text1.Text = UBound(tIndexDatas) & " Time:" & Timer - OnTimer & " RndLineNum:" & tRndLineNum & " RndLineAdd:" & tRndLineAdd
Open "ALL.TXT" For Input As #1
Seek #1, tIndexDatas(tRndLineNum)
Line Input #1, RdStr
Close #1
Text2.Text = RdStr
End Sub测试结果:ALL.TXT是文本格式的完整《红楼梦》。文件有:24788行消耗时间:0.6599731秒(解释状态,如果编译后能快许多。)随机选择一个行数:17490
该行地址:1242374该行的文本:" 脉息. 那王大夫诊了好一回儿,又换那只手也诊了,便同贾琏出来,到外间屋里坐下,"另外,对于我上面说的“补充插入法”,需要一些函数打基础。我会进一步编写出来,你不要着急。假如上面的函数不能令你满意,不妨等我的新函数。
这个方法目前遇到点难题,就是担心字符串能不能承受100M的数据量。原理是这样的:只要你知道第2999个换行的位置,你也就知道了第3000行的位置。而实质上问题就变成了“如何知道一个文件中第N个指定字符的字节位置”。
初步的实现的办法是先用String将整个文件读到变量里去(这里有个特别快的办法,可以在两个语句的周期搞定)。用Instr函数在该String里寻找第2999个换行,随即得到第3000行的开始。
由于你的文件特别大,所以我正在研究变通的办法。使这个办法能在100M大文件里可行。 不过,我觉得“Seek定位法”结合“添加补充法更新索引”是解决你这个问题的最佳途径。因为你的文件是只增不减,只追加不插入也不从中间去掉内容。所以特别适合该办法。目前完成了第一步,现在我正在接着写第二步实现索引的更新。