web-dev-qa-db-fra.com

Quel est le moyen le plus rapide de lire ligne par ligne de gros fichiers dans VBA?

Je pense avoir mis au point un moyen très efficace de lire de très gros fichiers ligne par ligne. S'il vous plaît dites-moi si vous connaissez un moyen meilleur/plus rapide ou voyez une marge d'amélioration. J'essaie de m'améliorer en codage, alors n'importe quel conseil serait Nice. J'espère que c'est quelque chose que d'autres personnes pourraient trouver utile, aussi.

Cela semble être quelque chose comme 8 fois plus rapide que d'utiliser l'entrée de ligne de mes tests.

'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

Merci pour le conseil!

12
Justin

Vous pouvez utiliser Scripting.FileSystemObject pour effectuer cette opération. À partir de Référence :

La méthode ReadLine permet à un script de lire des lignes individuelles dans un fichier texte. Pour utiliser cette méthode, ouvrez le fichier texte, puis configurez une boucle Do Do continue jusqu'à ce que la propriété AtEndOfStream soit définie sur True. (Cela signifie simplement que vous avez atteint la fin du fichier.) Dans la boucle Do, appelez la méthode ReadLine, stockez le contenu de la première ligne dans une variable, puis effectuez une action. Lorsque le script effectue une boucle, il laisse automatiquement tomber une ligne et lit la deuxième ligne du fichier dans la variable. Cela continuera jusqu'à ce que chaque ligne ait été lue (ou jusqu'à ce que le script quitte spécifiquement la boucle).

Et un exemple rapide:

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
12
Rodrigo

Mes deux centimes…

Il n’ya pas longtemps, j’avais besoin de lire de gros fichiers avec VBA et j’ai remarqué cette question. J'ai testé les trois approches permettant de lire les données d'un fichier afin de comparer sa vitesse et sa fiabilité pour une large gamme de tailles de fichiers et de longueurs de lignes. Les approches sont:

  1. Line Input instruction VBA
  2. Utilisation de l'objet système de fichiers (FSO)
  3. Utilisation de l'instruction Get VBA pour l'ensemble du fichier, puis analyse de la chaîne lue comme décrit dans les publications ici

Chaque cas de test comprend trois étapes:

  1. Configuration de scénario de test qui écrit un fichier texte contenant un nombre donné de lignes de la même longueur donnée, remplies par le motif de caractères connu.
  2. Test d'intégrité. Lisez chaque ligne de fichier et vérifiez sa longueur et son contenu.
  3. Test de vitesse de lecture de fichier. Lire chaque ligne du fichier répété 10 fois.

Comme vous pouvez le constater, l'étape 3 vérifie la vitesse réelle de lecture du fichier (comme indiqué dans la question), tandis que l'étape 2 vérifie l'intégrité de la lecture du fichier et simule donc les conditions réelles dans lesquelles l'analyse de chaîne est nécessaire.

Le tableau ci-dessous présente les résultats du test de vitesse de lecture de fichier. La taille du fichier est de 64 M octets pour tous les tests, et la longueur de la ligne de test varie de 2 octets (CRLF non compris) à 8 M octets.

No idea why it is not displayed any longer :(

CONCLUSION:

  1. Les trois méthodes sont fiables pour les fichiers volumineux avec des longueurs de ligne normales et anormales (comparez-les à Réponse de Graeme Howard )
  2. Les trois méthodes produisent une vitesse de lecture de fichier presque équivalente pour des longueurs de ligne normales
  3. “Superfast way” (Méthode n ° 3) fonctionne très bien pour les lignes extrêmement longues, contrairement aux deux autres.
  4. Tout cela est applicable à différents bureaux, différents PC, pour VBA et VB6
11
Argut

La saisie de ligne fonctionne bien pour les petits fichiers. Cependant, lorsque la taille des fichiers atteint environ 90k, Line Input saute partout et lit les données dans le mauvais ordre à partir du fichier source .

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

Leçon apprise - utilisez Scripting.FileSystemObject

5
Graeme Howard

Avec ce code, vous chargez le fichier en mémoire (en tant que grande chaîne), puis vous lisez cette chaîne ligne par ligne .

En utilisant Mid $ () et InStr (), vous lisez réellement le "fichier" deux fois, mais comme il est en mémoire, il n'y a pas de problème.
Je ne sais pas si VB's String a une longueur limitée (probablement pas), mais si les fichiers texte mesurent des centaines de mégaoctets, les performances risquent de chuter, en raison de l'utilisation de la mémoire virtuelle.

2
Nick Dandoulakis

Je pense que, dans le cas d’un fichier volumineux, l’utilisation d’un flux serait beaucoup plus efficace, car la consommation de mémoire serait très faible. 

Mais votre algorithme peut alterner entre l'utilisation d'un flux et le chargement de la totalité de la mémoire en fonction de la taille du fichier. Je ne serais pas surpris si l'un est seulement meilleur que l'autre sous certains critères.

1
Jeremy

Mon point de vue ... évidemment, vous devez faire quelque chose avec les données que vous avez lues. Si cela implique de l'écrire sur la feuille, ce sera terriblement lent avec une boucle For normale. Je suis arrivé à ce qui suit en me basant sur une récapitulation de certains éléments, plus de l’aide du site Web de Chip Pearson.

Lecture dans le fichier texte (en supposant que vous ne connaissiez pas la longueur de la plage qu'il va créer, seule la cellule de démarrage est donnée):

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

Inversement, si vous avez besoin d'écrire une plage dans un fichier texte, cela le fait rapidement en une instruction d'impression (remarque: le type de fichier 'Ouvrir' ici est en mode texte, pas binaire ... contrairement à la routine de lecture ci-dessus).

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
1
RexBarker

'vous pouvez modifier ci-dessus et lire le fichier complet en une fois .__, puis afficher chaque ligne comme indiqué ci-dessous

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
1
prakash b bajaj

Je voulais juste partager certains de mes résultats ... 

J'ai des fichiers texte, qui proviennent apparemment d'un système Linux. Je n'ai donc qu'une vbLFChr(10) à la fin de chaque ligne et pas vbCR/Chr(13).

_/ Note 1: 

  • Cela signifiait que la méthode Line Input lirait dans le fichier entier au lieu d'une seule ligne à la fois.

De mes recherches en testant des fichiers de petite taille (152 Ko) et des fichiers volumineux (2778LB), à la fois sur le réseau et hors réseau, j'ai constaté ce qui suit:

Open FileName For Input: Line Input était le le plus lent (Voir Note 1 ci-dessus)

Open FileName For Binary Access Read: Input était le le plus rapide pour lire tout le fichier

FSO.OpenTextFile: ReadLine était rapide , mais un peu plus lent que Binary Input

Note 2:

  • Si je devais juste vérifier l'en-tête du fichier (1-2 premières lignes) pour vérifier si j'avais le bon fichier/format, alors FSO.OpenTextFile était le le plus rapide, suivi de très près par Binary Input

  • L'inconvénient avec le Binary Input est que vous devez savoir combien de caractères tu veux lire. 

  • Sur les fichiers normaux, Line Input serait également un bon option également, mais je n’ai pas pu tester en raison deNote 1.

Note 3: 

  • De toute évidence, les fichiers sur le réseau présentaient la plus grande différence de vitesse de lecture. Ils ont également montré le plus grand avantage à lire le fichier une seconde fois (bien qu’il y ait certainement des mémoires tampons qui entrent en jeu ici).
0
Profex

Soyez prudent lorsque vous utilisez Application.Transpose avec un grand nombre de valeurs. Si vous transposez des valeurs dans une colonne, Excel suppose que vous supposez que vous les avez transposées à partir de lignes. 


Limite max. Colonne <Limite max. Ligne et affiche uniquement les premières valeurs (Limite max. Colonne) et après cela sera "N/A" 

0
Dumitru Daniel