Live-Forum - Die aktuellen Beiträge
Anzeige
Archiv - Navigation
1632to1636
Aktuelles Verzeichnis
Verzeichnis Index
Übersicht Verzeichnisse
Vorheriger Thread
Rückwärts Blättern
Nächster Thread
Vorwärts blättern
Anzeige
HERBERS
Excel-Forum (Archiv)
20+ Jahre Excel-Kompetenz: Von Anwendern, für Anwender
Inhaltsverzeichnis

Zeitoptimierung beim Auslesen von Textdateien

Zeitoptimierung beim Auslesen von Textdateien
25.07.2018 17:43:50
Textdateien
Hallo Ihr Experten,
wie schon der Titel sagt, habe ich ein zeitliches Problem beim Auslesen von Textdateien (.csv, .json, etc). Ein Datensatz dauert etwa 300ms (Öffnen+Schließen von 3 Dateien je 100ms), da ich aber tausende von Datensätzen habe wird der PC praktisch unbenutzbar für einen längeren Zeitraum - ein untragbarer Zustand.
Die üblichen Googleantworten helfen mir leider nicht weiter, da die Erzeugung der Textdateien schlampig realisiert wurde. Manche Daten stehen durch Tabstops in anderen Spalten als A beim Öffnen mit Excel und eine Anpassung bei der Erzeugung liegt außerhalb meiner Macht. Zur Korrektur sehe ich mich gezwungen über UsedRange die Daten ins Array nachzutragen, was ich nicht machen kann wenn ich die Dokumente nicht öffne.
Über bin über jeden Vorschlag zur Zeitersparnis dankbar. Am Ende muss die Funktion ein einspaltiges Array zurückgeben. Ich weiß im vorhinein nicht über wievlele Zeilen die Textdateien verfügen.
mfg Mohrrunkel
Unten stehende Funktion wird wie folgt im Makro aufgerufen:

aData = GetDefFile(CStr(aFiles(0, 1))) 'aFiles: Sammlung diverser Pfade+Dateinamen
'Stop
'For i = 1 To UBound(aData, 1)
'Debug.Print aData(i, 1)
'Next i

Private Function GetDefFile(ByRef sFile As String)
Dim wkbQ As Workbook
Dim rZelle As Range
Dim aData As Variant
On Error GoTo ErrorHandler
Set wkbQ = Workbooks.Open(sFile)
'Debug.Print sFile
On Error GoTo 0
With wkbQ.Sheets(1)
aData = .Range(.Cells(1, 1), .Cells(.Cells(.Rows.Count, 1).End(xlUp).Row, 1))
If .UsedRange.Columns.Count > 1 Then
For Each rZelle In .Range(.Cells(1, 2), .Cells(.Cells(.Rows.Count, 1).End(xlUp).Row, . _
UsedRange.Columns.Count)).SpecialCells(xlTextValues)
aData(rZelle.Row, 1) = aData(rZelle.Row, 1) & rZelle.Text
Next rZelle
End If
GetDefFile = aData
End With
Application.DisplayAlerts = False
wkbQ.Close
Application.DisplayAlerts = True
Exit Function
ErrorHandler:
GetDefFile = ""
End Function

11
Beiträge zum Forumthread
Beiträge zu diesem Forumthread

Betreff
Datum
Anwender
Anzeige
AW: Zeitoptimierung beim Auslesen von Textdateien
25.07.2018 17:48:51
Textdateien
Beispiel einer fehlerhaften Textdatei (Zeilen 22-25):
https://drive.google.com/file/d/1zeGWZDyrB6n7PkX6ksr5KFOXqyZH-XYW/view?usp=sharing
AW: alles auf einmal
25.07.2018 18:14:41
Fennek
Hallo,
der schnellste Weg dürfte sein, alle Txt/CSV Dateien in eine zu kopieren:

cmd:>copy *.txt alle.txt
und dann in ein Array laden.
mfg
AW: Zeitoptimierung beim Auslesen von Textdateien
25.07.2018 18:14:01
Textdateien
Hi
Kannst du vergessen, dass jemand die Datei von einem dubiosen Provider (=Google ;) anschaut. Bitte lade die Datei mittels File-Upload Funktion ins Forum.
Verstehe das Kernproblem und Ziel noch nicht ganz.
Sollte es notwendig sein die Datei zeilenweise einzulesen, dann würde ich es mit dieser Methode lösen:
Open "C:\Pfad\Datei.txt" For Input As #1

(Muster-Codes siehe Netz)
Das Problem der unbekannten Zeilenzahl kannst du z.B. mittels laufender Redimensionierung des Array lösen
Redim Preserve MeinArray(lngCounter)
lngCounter = lngCounter + 1
cu
Chris
Anzeige
AW: Zeitoptimierung beim Auslesen von Textdateien
25.07.2018 18:28:49
Textdateien
Gut, ich wollte die Datei möglichst original lassen (.json) und konnte sie in diesem Format nicht hier hochladen. Außerdem sah ich wenig Probleme darin ein im Web betrachtbares Objekt einzusehen, muss ja nicht runter geladen werden.
Hier nochmal für dich: https://www.herber.de/bbs/user/122892.txt
Ich probier mal eure Vorschläge aus, und melde mich. Danke
AW: Zeitoptimierung beim Auslesen von Textdateien
25.07.2018 19:35:40
Textdateien
Hallo,
ich denke dass die Methode von Fennek anderen Overhead mit sich bringt. Du müsstest ja alle Dateien mit Anfang bzw. Ende versehen. Sonst kannst Du ja nix mehr richtig zuordnen, wenn die einfach untereinander stehen, wenn ich es richtig verstanden habe. Da geht dann Zeit für die Datenorganisation drauf. (Das alles wieder richtig auseinander zu fisseln.) Abgesehen davon bleibt da auch nur der Weg über eine Tabelle (langsam) oder die richtige Dimensionierung eines Arrays.
Das vorgeschlagene Redim Preserve von Chris ist wahrscheinlich schneller als Deine jetzige Methode, weil alles im Speicher stattfinden kann. Aber es ist unterm Strich für Dein Anliegen nicht praktikabel denke ich.
Redim Preserve speichert den Inhalt des zu vergrößernden Arrays zwischen, erzeugt ein neues und schreibt das Zwischengespreicherte da wieder rein. Für tausende von Textdateien mit wahrscheinlich hunderttausenden von Zeilen, bedeutet das einen gewaltigen Zeitverlust, weil der Vorgang für jede einzelne Zeile angewendet werden muss.
Du kannst mit folgender Funktion allerdings direkt die Zeilen in einer Text Datei bestimmen und Deine Arrays dann in einem Schritt nur mit Redim richtig dimensionieren. Dabei wird das bei der Deklaration nicht dimensionierte Array direkt auf so viele Elemente vergrößert, wie die aktuell bearbeitete Datei Zeilen hat.
Hier die Funktion zum Auslesen, wieviele Zeilen eine Textdatei hat:

Public Function GetNumberOfRowsInTextFile(ByVal strFile As String) As Long
'Quelle:
'Uwe Fabig - 13.02.2013, 11:30
'https://www.xing.com/communities/posts/anzahl-zeilen-einer-textdatei-ermitteln-1002110449
Dim fso As Object
Dim ts As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.OpenTextFile(strFile)
ts.ReadAll
GetNumberOfRowsInCsvFile = ts.Line
Set fso = Nothing
Set ts = Nothing
End Function

Da kannst Du Deine Text Datei dann zeilenweise einlesen und während des Einlesens die unerwünschten Zeichen entfernen. Tabs wirst Du mit folgender Codezeile los:

zielArry(x) = Replace(zielArry(x), Chr(9), "")

Den Rest Deines Datei Handlings musst Du drum rum stricken.
Viele Grüße,
Zwenn
Anzeige
AW: Zeitoptimierung beim Auslesen von Textdateien
25.07.2018 20:50:20
Textdateien
Ups,
hatte die Funktion umbenannt, um sie allgemeiner zu bennen und vergessen das auch in der Zeile für den Rückgabewert zu ändern:

Public Function GetNumberOfRowsInTextFile(ByVal strFile As String) As Long
'Quelle:
'Uwe Fabig - 13.02.2013, 11:30
'https://www.xing.com/communities/posts/anzahl-zeilen-einer-textdatei-ermitteln-1002110449
Dim fso As Object
Dim ts As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.OpenTextFile(strFile)
ts.ReadAll
GetNumberOfRowsInTextFile = ts.Line
Set fso = Nothing
Set ts = Nothing
End Function

Anzeige
AW: Zeitoptimierung beim Auslesen von Textdateien
25.07.2018 21:03:14
Textdateien
Auch dir danke Zwenn.
Du hast recht, alles einfach in eine Datei stopfen ist unpraktikabel. Auch bei den drei zusammengehörenden Dateien für einen Datensatz gibt es Wiederholungen von IDs usw..
Habe es mit dem Vorschlag von Chris versucht, komischerweise habe ich bei 10% der Dateien ein Problem: Es zerlegt mir die Zeilen komplett, scheinbar zählt ein einfach Space (Chr(32)) manchmal als Zeilenumbruch und manchmal icht. Im Endeffekt ist dann der Eintrag zur ID nicht mehr auf der selben Zeile wie die ID was problematisch ist. Ansonsten funktioniert das deutlich einfacher wie gedacht.
Daher werd ich mir mal deinen Vorschlag anschauen. Melde mich.
Anzeige
AW: Zeitoptimierung beim Auslesen von Textdateien
25.07.2018 21:31:31
Textdateien
Deine Antwort hat mich jetzt erstmal weiter gebracht.
Das entfernen der Tabstops war nicht das Problem wenn ich das ganze als Zeile gehabt hätte, da ich es aber ursprünglich von Excel habe öffnen lassen, hatte es mir Daten in andere Spalten verschoben und mein Array zwei- oder mehrspalting gemacht, was problematisch war.
Wenn ich über das FSO die Datei auslese (Zeile für Zeile) habe ich zumindest schonmal was ich _ brauche. Keine zerstückelten Datenzeilen, keine Tabstops sondern den Inhalt so wie ich ihn im Notepad++ auch sehe. Schöner ginge es nur wenn ich mit einem Satz alle Zeilen der Datei ins Array bekäme. Zeit muss ich noch testen.

Sub test5(sFile As String)
Dim FSO As FileSystemObject
Dim TextDat As Object
Dim i As Long
Dim aData As Variant
Set FSO = CreateObject("Scripting.FileSystemObject")
Set TextDat = FSO.OpenTextFile(sFile)
Do While TextDat.AtEndOfStream  True
aData(i) = TextDat.ReadLine
i = i + 1
Loop
TextDat.Close
End Sub

Anzeige
Gelöst
25.07.2018 22:16:08
Mohrrunkel
Danke an alle Beteiligten.
Mein restliches Makro war bereits darauf ausgelegt alles im Speicher zu berechnen, darum braucht ich ja das Ergebnis wieder als Array. Für einen Datesatz auswerten hattte es nur 2-3 ms gebraucht, trotz eine Millionen Schleifen. Nur das Öffnen war der Knackpunkt mit 300 ms pro Satz.
Über das FSO und mit Zwenns Formel zum Bestimmen der Zeilen hab ich nun eine Gesamtbearbeitungszeit von 5s statt 20 min und dabei habe ich mir noch extra jeden Namen des Datensatzes einzeln in eine Spalte untereinander auflisten lassen, zur Kontrolle, was ja auch Zeit kostet.
Ich bin erstaunt wieviel das gebracht hat, mehr als ich zu hoffen gewagt hatte.
Herzlichen Dank nochmal und euch einen schönen Abend noch.
Private Function GetDefFile(ByRef sFile As String)
Dim aData As Variant
Dim FSO As FileSystemObject
Dim TextDat As Object
Dim i As Long
Set FSO = CreateObject("Scripting.FileSystemObject")
Set TextDat = FSO.OpenTextFile(sFile)
ReDim aData(GetNumberOfRowsInTextFile(sFile) - 1)
Do While TextDat.AtEndOfStream  True
aData(i) = TextDat.ReadLine
i = i + 1
Loop
TextDat.Close
GetDefFile = aData
End Function

Anzeige
AW: Variante 2
26.07.2018 08:37:17
Fennek
Hallo,
könntest Du diesen Ansatz testen:

Set FSO = CreateObject("Scripting.FileSystemObject")
Tx = Split(FSO.opentextfile("z:\data.txt").readall, vbCrLf)
' Tx ist eine 1-dim Array
mfg
AW: Variante 2
26.07.2018 10:32:49
Zwenn
Hallo zusammen,
@Mohrrunkel
Schön dass sich die Zeit für die Bearbeitung so drastisch senken ließ.
@Fennek
Das ist ja nice. Die Methode ReadAll kannte ich bisher nicht. Mir kam nur ein ADO Stream in den Sinn, um eine Textdatei in einem Rutsch einzulesen. Allerdings auch nur, weil ich mich damit grade vorgestern beschäftigt habe. Ich hatte aus Mangel an Erfahrung damit nur keine Idee, wie man mit so einem Stream dann weiter arbeiten könnte, bzw. ob das unterm Strich dann wirklich schneller wäre, wenn man den Stream dann nochmal zeilenweise verarbeiten muss. Aber das FSO mit der Methode ReadAll ist für diesen Fall wahrscheinlich perfekt, denke ich.
Viele Grüße,
Zwenn
Anzeige

301 Forumthreads zu ähnlichen Themen

Anzeige
Anzeige
Anzeige

Beliebteste Forumthreads (12 Monate)

Anzeige

Beliebteste Forumthreads (12 Monate)

Anzeige
Anzeige
Anzeige