什么是在VBA中逐行读取大文件的超快方法?

时间:2009-09-04 01:04:37

标签: vba file-io

我相信我已经提出了一种非常有效的方法来逐行读取非常大的文件。如果您知道更好/更快的方式或者看到改进的空间,请告诉我。我正在努力改进编码,所以你提出的任何建议都会很好。希望这也是其他人可能会觉得有用的东西。

它似乎比我在测试中使用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

感谢您的建议!

9 个答案:

答案 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读取大文件并注意到这个问题。我测试了从文件中读取数据的三种方法,以比较各种文件大小和行长度的速度和可靠性。方法是:

  1. Line Input VBA声明
  2. 使用文件系统对象(FSO)
  3. 对整个文件使用Get VBA语句,然后按照此处的帖子中的说明解析读取的字符串
  4. 每个测试用例包含三个步骤:

    1. 测试用例设置,用于写入包含由已知字符模式填充的给定长度的给定行数的文本文件。
    2. 完整性测试。阅读每个文件行并验证其长度和内容。
    3. 文件读取速度测试。重复读取文件的每一行10次。
    4. 正如您所注意到的,步骤#3验证了真正的文件读取速度(如问题中所述),而步骤#2验证文件读取完整性,因此在需要进行字符串解析时模拟真实条件。

      下图显示了文件读取速度测试的测试结果。所有测试的文件大小为64M字节,测试的行长度从2个字节(不包括CRLF)到8M字节不等。

      No idea why it is not displayed any longer :(

      结论:

      1. 这三种方法对于行长正常和异常的大文件都是可靠的(请与Graeme Howard’s answer比较)
      2. 所有这三种方法对于正常行长度产生几乎相等的文件读取速度
      3. “Superfast way”(方法#3)适用于极长的线路,而其他两种则不行。
      4. 所有这些适用于不同的办公室,不同的PC,适用于VBA和VB6

答案 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:

     
      
  • 很明显,网络上的文件在读取速度上显示出最大的差异。他们还显示出第二次读取文件的最大好处(尽管这里肯定有一些内存缓冲区在起作用)。
  •