我相信我已经提出了一种非常有效的方法来逐行读取非常大的文件。如果您知道更好/更快的方式或者看到改进的空间,请告诉我。我正在努力改进编码,所以你提出的任何建议都会很好。希望这也是其他人可能会觉得有用的东西。
它似乎比我在测试中使用Line Input快8倍。
'This function reads a file into a string. '
'I found this in the book Programming Excel with VBA and .NET. '
Public Function QuickRead(FName As String) As String
Dim I As Integer
Dim res As String
Dim l As Long
I = FreeFile
l = FileLen(FName)
res = Space(l)
Open FName For Binary Access Read As #I
Get #I, , res
Close I
QuickRead = res
End Function
'This function works like the Line Input statement'
Public Sub QRLineInput( _
ByRef strFileData As String, _
ByRef lngFilePosition As Long, _
ByRef strOutputString, _
ByRef blnEOF As Boolean _
)
On Error GoTo LastLine
strOutputString = Mid$(strFileData, lngFilePosition, _
InStr(lngFilePosition, strFileData, vbNewLine) - lngFilePosition)
lngFilePosition = InStr(lngFilePosition, strFileData, vbNewLine) + 2
Exit Sub
LastLine:
blnEOF = True
End Sub
Sub Test()
Dim strFilePathName As String: strFilePathName = "C:\Fld\File.txt"
Dim strFile As String
Dim lngPos As Long
Dim blnEOF As Boolean
Dim strFileLine As String
strFile = QuickRead(strFilePathName) & vbNewLine
lngPos = 1
Do Until blnEOF
Call QRLineInput(strFile, lngPos, strFileLine, blnEOF)
Loop
End Sub
感谢您的建议!
答案 0 :(得分:13)
您可以使用Scripting.FileSystemObject来执行该操作。 来自Reference:
ReadLine方法允许脚本读取文本文件中的各个行。要使用此方法,请打开文本文件,然后设置一个Do循环,该循环一直持续到AtEndOfStream属性为True。 (这只是意味着您已到达文件的末尾。)在Do循环中,调用ReadLine方法,将第一行的内容存储在变量中,然后执行某些操作。当脚本循环时,它将自动下拉一行并将文件的第二行读入变量。这将继续,直到每行都被读取(或直到脚本专门退出循环)。
一个简单的例子:
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\FSO\ServerList.txt", 1)
Do Until objFile.AtEndOfStream
strLine = objFile.ReadLine
MsgBox strLine
Loop
objFile.Close
答案 1 :(得分:11)
我的两分钱......
不久前我需要使用VBA读取大文件并注意到这个问题。我测试了从文件中读取数据的三种方法,以比较各种文件大小和行长度的速度和可靠性。方法是:
Line Input
VBA声明Get
VBA语句,然后按照此处的帖子中的说明解析读取的字符串每个测试用例包含三个步骤:
正如您所注意到的,步骤#3验证了真正的文件读取速度(如问题中所述),而步骤#2验证文件读取完整性,因此在需要进行字符串解析时模拟真实条件。
下图显示了文件读取速度测试的测试结果。所有测试的文件大小为64M字节,测试的行长度从2个字节(不包括CRLF)到8M字节不等。
结论:
答案 2 :(得分:5)
线路输入适用于小文件。但是,当文件大小达到大约90k时,行输入会在整个地方跳转,并从源文件中以错误的顺序读取数据。 我用不同的文件大小测试了它:
49k = ok
60k = ok
78k = ok
85k = ok
93k = error
101k = error
127k = error
156k = error
获得的经验教训 - 使用Scripting.FileSystemObject
答案 3 :(得分:2)
使用该代码将文件加载到内存中(作为一个大字符串),然后逐行读取该字符串。
通过使用Mid $()和InStr(),您实际上读了两次“文件”,但由于它在内存中,所以没有问题。
我不知道VB的字符串是否有长度限制(可能不是),但如果文本文件的大小为数百兆字节,则由于虚拟内存的使用,可能会出现性能下降。
答案 4 :(得分:1)
我认为,在大型文件中,使用流的方案效率会更高,因为内存消耗非常小。
但是你的算法可以在使用流和基于文件大小加载内存中的整个事物之间交替。如果一个人在某些标准下只比另一个好,我不会感到惊讶。
答案 5 :(得分:1)
'您可以在上面修改并一次性读取完整文件 然后显示如下所示的每一行
Option Explicit
Public Function QuickRead(FName As String) As Variant
Dim i As Integer
Dim res As String
Dim l As Long
Dim v As Variant
i = FreeFile
l = FileLen(FName)
res = Space(l)
Open FName For Binary Access Read As #i
Get #i, , res
Close i
'split the file with vbcrlf
QuickRead = Split(res, vbCrLf)
End Function
Sub Test()
' you can replace file for "c:\writename.txt to any file name you desire
Dim strFilePathName As String: strFilePathName = "C:\writename.txt"
Dim strFileLine As String
Dim v As Variant
Dim i As Long
v = QuickRead(strFilePathName)
For i = 0 To UBound(v)
MsgBox v(i)
Next
End Sub
答案 6 :(得分:1)
我对它的看法......很明显,你必须对你读过的数据做些什么。如果它涉及将它写入工作表,那么正常的For循环将会致命的慢。我基于对那里的一些项目的重复,以及来自Chip Pearson网站的一些帮助,想出了以下内容。
阅读文本文件(假设您不知道它将创建的范围的长度,因此只给出了startingCell):
Public Sub ReadInPlainText(startCell As Range, Optional textfilename As Variant)
If IsMissing(textfilename) Then textfilename = Application.GetOpenFilename("All Files (*.*), *.*", , "Select Text File to Read")
If textfilename = "" Then Exit Sub
Dim filelength As Long
Dim filenumber As Integer
filenumber = FreeFile
filelength = filelen(textfilename)
Dim text As String
Dim textlines As Variant
Open textfilename For Binary Access Read As filenumber
text = Space(filelength)
Get #filenumber, , text
'split the file with vbcrlf
textlines = Split(text, vbCrLf)
'output to range
Dim outputRange As Range
Set outputRange = startCell
Set outputRange = outputRange.Resize(UBound(textlines), 1)
outputRange.Value = Application.Transpose(textlines)
Close filenumber
End Sub
相反,如果你需要写一个文本文件的范围,这可以在一个打印语句中快速完成(注意:文件'打开'这里输入的是文本模式,而不是二进制..不像上面的阅读程序。)
Public Sub WriteRangeAsPlainText(ExportRange As Range, Optional textfilename As Variant)
If IsMissing(textfilename) Then textfilename = Application.GetSaveAsFilename(FileFilter:="Text Files (*.txt), *.txt")
If textfilename = "" Then Exit Sub
Dim filenumber As Integer
filenumber = FreeFile
Open textfilename For Output As filenumber
Dim textlines() As Variant, outputvar As Variant
textlines = Application.Transpose(ExportRange.Value)
outputvar = Join(textlines, vbCrLf)
Print #filenumber, outputvar
Close filenumber
End Sub
答案 7 :(得分:0)
使用具有大量值的Application.Transpose时要小心。如果将值转置为列,则excel将假定您假设已从行转置它们。
最大列限制<最大行限制,它只显示第一个(最大列限制)值,之后的数字将是" N / A"
答案 8 :(得分:0)
我只想分享一些结果...
我有文本文件,这些文件显然来自Linux系统,因此每行末尾只有vbLF
/ Chr(10)
,而没有vbCR
/ {{1} }。
注1:
- 这意味着
Chr(13)
方法将读取整个文件,而不是一次读取一行。
通过对网络上和网络外的小文件(152KB)和大文件(2778LB)的研究测试,我发现了以下内容:
Line Input
是最慢的 (请参见上面的注释1 )
Open FileName For Input: Line Input
是读取整个文件的最快
Open FileName For Binary Access Read: Input
快速,但比FSO.OpenTextFile: ReadLine
注释2:
如果我只需要检查文件头(前1-2行)以检查文件/格式是否正确,则
Binary Input
是 最快 ,其后紧跟FSO.OpenTextFile
。
Binary Input
的缺点是您必须知道多少个字符 你想读。- 在普通文件上,
Binary Input
也是不错的选择 选项也可以,但是由于 注释1 而无法测试。
注释3:
- 很明显,网络上的文件在读取速度上显示出最大的差异。他们还显示出第二次读取文件的最大好处(尽管这里肯定有一些内存缓冲区在起作用)。