2009-09-04 286 views
9

我相信我已經想出了一種非常有效的方式來逐行讀取非常大的文件。請告訴我,如果你知道更好/更快的方式或看到改進的餘地。我試圖在編碼方面做得更好,所以你的任何建議都會很好。希望這是其他人可能會覺得有用的東西。什麼是在VBA中逐行讀取大文件的超快方法?

它似乎比使用我的測試中的線路輸入快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 

感謝您的建議!

回答

2

使用該代碼,您將文件加載到內存中(作爲大字符串),然後逐行讀取該字符串。

通過使用Mid $()和InStr(),你實際上讀了兩次「文件」,但由於它在內存中,所以沒有問題。
我不知道VB的字符串是否具有長度限制(可能不是),但如果文本文件的大小爲幾百兆字節,則由於虛擬內存使用情況,可能會看到性能下降。

+0

這是一個非常好的點。我非常天真地使用兩個非常誇張的東西。我使用的文件的大小大約是五到十兆,並且從不超過五十。 – Justin 2009-09-09 14:05:01

+1

在VB和VBA中,可變長度字符串**的**最大長度約爲。 ** 20億字符**(又名2GB)。 (來源:[VBA](https://msdn.microsoft.com/en-us/vba/language-reference-vba/articles/data-type-summary)&[VB](https://docs.microsoft。 com/en-us/dotnet/visual-basic/language-reference/data-types/data-type-summary)) – ashleedawg 2018-01-02 07:00:36

+0

@ashleedawg,感謝您的信息。稍微修正:由於每個字符的大小爲2個字節(unicode),所以限制爲4GB。 – 2018-01-02 10:12:30

1

我認爲,在一個大文件場景中使用流會更有效率,因爲內存消耗會非常小。

但是,您的算法可以在使用流和基於文件大小加載內存中的整個事物之間交替。如果一個人在某些標準下只比另一個更好,我不會感到驚訝。

+0

這也是一個很好的觀點,我發現,如果您只需要從文件的開頭讀取信息,就更是如此;在這種情況下,使用流將會好很多。另外,你提出內存問題是很好的,因爲我在編程時並沒有特別意識到內存使用的影響,但我想這只是我成爲新手的一個後果。 – Justin 2009-09-09 14:09:47

11

您可以使用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 
+2

這是另一個有趣的觀點。我的(相對有限的)測試表明,這實際上是三者中最慢的方法。使用FSO作爲流打開文件比使用整型文件句柄打開需要更多的時間,並且將整個文件讀入字符串花費的時間大致相同。當它實際上逐行閱讀時,它也變得更慢......如果我沒有記錯的話,無論如何;自從我做了測試併發布了所有這一切以來,這已經有一段時間了。 – Justin 2009-09-09 14:15:03

+1

您是否僅測試文件讀取或文件讀取和連接?我編寫的應用程序使用filesystemobject來加載超大文件(超過400MB),並且不會花太長時間(加載整個文件的時間不會超過幾秒)。 請記住,字符串連接總是很慢,除非您使用數組實現連接。 – Rodrigo 2009-09-09 19:13:51

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 
5

線路輸入作品適用於小文件。但是,當文件大小達到90k左右時,線路輸入會跳到所有位置,並以錯誤的順序從源文件中讀取數據。 我有不同的filesizes測試它:

49k = ok 
60k = ok 
78k = ok 
85k = ok 
93k = error 
101k = error 
127k = error 
156k = error 

教訓教訓 - 使用Scripting.FileSystemObject的

+1

如果文件有任何種類的結構,記錄集非常方便。您可以使用Microsoft文本驅動程序來創建一個。 – Fionnuala 2012-10-03 12:53:04

9

我的兩分錢......

不久前,我需要閱讀使用VBA大文件,並注意到了這個問題。我測試了三種方法來從文件中讀取數據,以比較其文件大小和行長度的速度和可靠性。該方法是:

  1. Line Input VBA語句
  2. 使用文件系統對象(FSO)
  3. 使用Get VBA語句整個文件,然後解析在帖子這裏
描述讀取字符串

每個測試用例包括三個步驟:

  1. 測試案例設置是寫入一個文本文件,其中包含由已知字符模式填充的相同給定長度的給定數量的行。
  2. 完整性測試。讀取每個文件行並驗證其長度和內容。
  3. 文件讀取速度測試。重複讀取文件的每一行10次。

正如你可以看到,第3步驗證其真實文件讀取速度(如問的問題),而第2步驗證文件讀取的完整性,並因此需要字符串解析時,模擬真實情況。

下圖顯示了文件讀取速度測試的測試結果。對於所有測試,文件大小爲64M字節,並且測試線長度不同,從2字節(不包括CRLF)到8M字節不等。

No idea why it is not displayed any longer :(

結論:

  1. 所有這三種方法是可靠與正常和異常的線路長度大文件(請比較Graeme Howard’s answer
  2. 所有這三種方法產生幾乎等同文件閱讀速度爲正常線長度
  3. 「超快速方式」(方法#3)適用於非常長的線路,而其他兩種則不適用。
  4. 這一切適用於不同的辦公室,不同的PC,對VBA和VB6
+1

方法3的示例:http://stackoverflow.com/a/19412682/4691433 – puzzlepiece87 2015-08-04 17:39:08

+0

優秀的信息,謝謝。 – ashleedawg 2018-01-02 07:01:16

0

我就帶它......很明顯,你必須做你讀入。該數據的東西,如果它涉及將它寫到表單上,這會在正常的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 
0

使用具有大量值的Application.Transpose時要小心。如果你將值轉置到一列,excel會假設你假設你將它們從行中移走。


最大列限制<最大行限制,並且它將只顯示第一(最大列限制)值,以及後anithing,這將是「N/A」

相關問題