 |
Das deutsche QBasic- und FreeBASIC-Forum Für euch erreichbar unter qb-forum.de, fb-forum.de und freebasic-forum.de!
|
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen |
Autor |
Nachricht |
Muecke Gast
|
Verfasst am: 06.01.2014, 16:19 Titel: Open Datei auslesen erst ab Zeile i |
|
|
Hallo miteinander,
ich glaube das ich das etwas umständlich mache ich habe aber leider nichts gefunden das mich weiterbringt.
ich möchte aus einer Datei eine Bestimmte Zeile (i) auslesen
wie mache ich das am besten?
ich habe hier einen Ansatz, glaube aber das ich da suboptimal unterwegs bin mit.
Code: | Open filename FOR INPUT ENCODING "ASCII" AS #DNr
For i As Integer = 1 To 4
Line INPUT #DNr, Zeile
Next
Close #DNr |
|
|
Nach oben |
|
 |
Jojo alter Rang

Anmeldungsdatum: 12.02.2005 Beiträge: 9736 Wohnort: Neben der Festplatte
|
Verfasst am: 06.01.2014, 16:35 Titel: |
|
|
Einfacher als das geht's wirklich nicht, und Performance wird auch nicht dein Problem sein, oder?  _________________ » Die Mathematik wurde geschaffen, um Probleme zu lösen, die es nicht gäbe, wenn die Mathematik nicht erschaffen worden wäre.
 |
|
Nach oben |
|
 |
Muecke Gast
|
Verfasst am: 06.01.2014, 16:40 Titel: |
|
|
du hast recht die Performance ist nicht wirklich mein Problem .
dachte nur das es vielleicht die Möglichkeit gibt
so was in die Richtung
Code: |
Open filename FOR INPUT ENCODING "ASCII" AS #DNr
Line INPUT #DNr, 4, Zeile
Close #DNr |
oder so was:
Code: |
Open filename FOR INPUT ENCODING "ASCII" AS #DNr, 4
Line INPUT #DNr, Zeile
Close #DNr |
die 4 steht dafür welche Zeilen Nr. aufgerufen werden soll.
dann hätte man die lästige For Schleife weg.
Doch wenn ich da schon richtig unterwegs bin perfekt. |
|
Nach oben |
|
 |
grindstone
Anmeldungsdatum: 03.10.2010 Beiträge: 1279 Wohnort: Ruhrpott
|
Verfasst am: 06.01.2014, 18:13 Titel: |
|
|
@Muecke: Wenn du nur eine oder einige wenige Zeilen oder jede Zeile nur einmal brauchst, ist dein erster Ansatz die beste Lösung. Wenn du dagegen häufiger darauf zugreifen willst und/oder in einer anderen Rehenfolge als sie in der Datei stehen, ist es günstiger, vorher die ganze Datei zeilenweise in ein Array zu kopieren und dann über den entsprechenden Index darauf zuzugreifen.
Gruß
grindstone _________________ For ein halbes Jahr wuste ich nich mahl wie man Proggramira schreibt. Jetzt bin ich einen! |
|
Nach oben |
|
 |
Muecke Gast
|
Verfasst am: 07.01.2014, 00:02 Titel: |
|
|
OK, ich habe nun entschlossen das ich die Datei vollständig in ein 2D Arrey laden werde, da ich öfter auf die Daten in unterschiedlicher reihen folge zugreifen möchte
nur das teil ist extrem langsam
meine Datei:
Zeilen: 360
Spalten: 1011
Größe 1.370 KB
das Teil ist wirklich extrem langsam
und das ist noch eine der Kleinen Datei ich habe noch Daten Sätze hier die Zeilen von über 12652 haben.
die Daten sind dann um die 54.641 KB Groß.
wie kann ich das denn beschleunigen?
das ist der Test Code:
Code: | Declare Sub DateiInArrey (filename As String, Datei_Array() As String)
Declare Sub explode (Separator As String, Strg As String, explode_Array() AS String)
Dim As String filename
filename = "C:\Users\00\Desktop\Data\Dunlop001.csv"
SCREENRES 1024, 768, ,2
ReDim As String Daten3D(1)
Print "Datei Einlesen Starten:"
DateiInArrey filename, Daten3D()
Print "Datei Einlesen ENDE:"
Sleep: END
Sub DateiInArrey (Datei As String, Datei_Array() As String)
Dim DNr As Integer = FreeFile
Dim As String Text, Separator
Dim As Integer x, i, n, anfptr, endptr
Open Datei FOR INPUT ENCODING "ASCII" AS #DNr
x=0
DO
x + = 1
LINE INPUT #DNr, Text
i = 0
anfptr = 1 'pointer auf den anfang des strings setzen
Separator = ","
Do 'string zerlegen
i + = 1 'zähler erhöhen
ReDim Preserve Datei_Array(x, i) 'array vergrößern
endptr = InStr(anfptr, Text, Separator) 'endpointer auf nächsten separator setzen
Datei_Array(x, i) = LTrim(RTrim(Mid(Text, anfptr, endptr - anfptr))) 'teilstring in array schreiben
anfptr = endptr + Len(Separator) 'anfangspointer hinter den separator setzen (anfang des nächsten teilstrings oder stringende)
If i>n Then n=i
Cls
Locate 4, 10: Print "Datei:", Datei
Locate 5, 10: Print "x:", x
Locate 6, 10: Print "i:", i, "Höchster Wert: ";n
Locate 7, 10: Print "anfptr:", anfptr
Locate 8, 10: Print "endptr:", endptr
Locate 9, 10: Print "Datei_Array(x,i):", Datei_Array(x,i)
screensync ' wartet mit der Programmausführung auf eine Bildschirmaktualisierung. Damit wird ein Flimmern des Bildschirms vermieden.
Loop Until endptr <= 0 'weitermachen, bis der string zuende ist (wenn Seperator nict gefunden wierd wir 0 ausgegeben)
Loop until eof(1) ' bis Dateiende erreicht wurde
Close #DNr
End Sub
Sub explode (Separator As String, Strg As String, explode_Array() AS String)
Dim As Integer anfptr, endptr, x ' Variablen definiren als Zahlen nur für die SUB sonst nirgens
x = 0
anfptr = 1 'pointer auf den anfang des strings setzen
Do 'string zerlegen
x + = 1 'zähler erhöhen
ReDim Preserve explode_Array(x) 'array vergrößern
endptr = InStr(anfptr, Strg, Separator) 'endpointer auf nächsten separator setzen
explode_Array(x) = LTrim(RTrim(Mid(Strg, anfptr, endptr - anfptr))) 'teilstring in array schreiben
anfptr = endptr + Len(Separator) 'anfangspointer hinter den separator setzen (anfang des nächsten teilstrings oder stringende)
Loop Until endptr <= 0 'weitermachen, bis der string zuende ist (wenn Seperator nict gefunden wierd wir 0 ausgegeben)
End Sub
|
|
|
Nach oben |
|
 |
Jojo alter Rang

Anmeldungsdatum: 12.02.2005 Beiträge: 9736 Wohnort: Neben der Festplatte
|
Verfasst am: 07.01.2014, 00:27 Titel: |
|
|
Du hast so ziemlich jede mögliche teure Operation direkt in deiner innersten Schleife, natürlich ist das langsam.
* ReDim Preserve kopiert in jedem Schleifendurchlauf das komplette (jedes mal wachsende) Array (sofern FB sowas nicht durch Arrays mit Kapazität != tatsächlich benötigter Platz optimiert, was ich nicht glaube). Sinnvoller wäre es beispielsweise, Das Array immer in 100er-Schritten zu vergrößern, sodass das Umkopieren nur noch bei jedem 100. Schleifendurchlauf stattfindet. Selbiges gilt übrigens für das Redim Preserve in der explode-Methode.
* Ltrim(Rtrim(Mid(...))) erzeugt drei temporäre String-Objekte, das ist ebenfalls teuer. Sinnvoller wäre es, das Ergebnis von Ltrim und Rtrim jeweils "von Hand" zu berechnen (also jeweils ab anfptr und endptr nach Leerzeichen suchen) und dann die gefundenen Stellen an Mid übergeben. Und wenn dir das zu viel Arbeit ist, verwende wenigstens Trim() statt Ltrim(Rtrim()), um ein temporäres Objekt zu sparen.
* Jegliche Bildschirmausgaben sind immer teuer, die sollten auch nicht nach jeder Zeile gemacht werden.
* Screensync verlangsamt dein Programm künstlich, da es für jede eingelesene Zeile bis zu 1/60 Sekunde verbrät.
Die ersten beiden Punkte lassen sich durch besseren Code lösen, die letzten beiden Punkte haben einfach überhaupt nichts in einer inneren Schleife verloren. Versuch erst mal, den Ausgabecode aus der inneren Schleife zu entfernen, evtl reicht das schon und du musst dich gar nicht um die ersten beiden Punkte kümmern - besser wäre es aber. _________________ » Die Mathematik wurde geschaffen, um Probleme zu lösen, die es nicht gäbe, wenn die Mathematik nicht erschaffen worden wäre.
 |
|
Nach oben |
|
 |
Muecke Gast
|
Verfasst am: 07.01.2014, 00:42 Titel: |
|
|
OK das muss ich mir morgen noch mal genauer anschauen,
die Ausgabe in der Schleife habe ich eingebaut da ich dachte das mein Programm abgestürzt ist und ich den Fehler finden wollte da es einfach nicht mehr reagierte
bei der Ausgabe ist mir dann aufgefallen das es die Verarbeitens Dauer ist, und nicht das Prog abgestürzt ist
werde das morgen Optimieren nach deinem vorschlagen.
Danke für die hinweise. |
|
Nach oben |
|
 |
Sebastian Administrator

Anmeldungsdatum: 10.09.2004 Beiträge: 5969 Wohnort: Deutschland
|
Verfasst am: 07.01.2014, 01:25 Titel: Verkettete Liste statt Array und REDIM PRESERVE benutzen |
|
|
Hallo,
eine große Datei mit sehr vielen Zeilen in ein Array einzulesen, indem man es mit jeder Zeile via REDIM PRESERVE um 1 Element vergrößert, ist auch extrem ineffizient.
Dazu muss man wissen, wie Arrays und REDIM PRESERVE "unter der Haube" funktionieren.
Wenn du ein Array erstellst, wird im Speicher ein Block reserviert mit der Größe [Größe eines Elements] X [Anzahl der Elemente] = [Blockgröße].
Vor und hinter dem Array stehen irgendwelche anderen Daten. Aber für das Array reicht der Platz.
Wenn du jetzt das Array etwas größer machen möchtest, kann das Programm nicht einfach hintendran weiterschreiben. Weil da höchstwahrscheinlich irgendwas anderes steht, was nicht einfach so überschrieben werden darf.
D.h. wenn dein Array bisher 100 Byte belegt und du jetzt z. B. einen Integer mit 4 Byte hinzufügen möchtest, fordert das Programm eine neue Speicherlücke mit 104 Bytes an. Anschließend wird das alte Array (100 Byte) in das neue Array kopiert. Das alte Array wird gelöscht.
Wenn du ein Array 10.000x so vergrößerst, wird 10.000x das gesamte Array in ein neues kopiert und das alte gelöscht.
Das liegt daran, dass Arrays immer Blöcke "am Stück" sind und nicht gestückelt vorliegen. Daraus ergibt sich folgende Eigenschaft: Wenn ein Array im Speicher an der Adresse 1000 anfängt. Und ein Arrayelement 4 Bytes lang ist (z. B. ein INTEGER), dann steht das 1. Arrayelement an Adresse 1000, das zweite an Adresse 1004, das dritte an 1008 usw. Du kannst dann einfach die Basisadresse mit einem Offset addieren, um zum gewünschten Element zu kommen. Dadurch gehen lesende Zugriffe auf eine beliebige Stelle des Arrays extrem schnell. Wenn ich weiß, wo das Array anfängt im Speicher und wie lang 1 einzelnes Element ist, weiß ich spielend einfach, wo das Element 2384 oder das Element 83930 steht. Das muss man nicht suchen, sondern kann die Stelle einfach ausrechnen.
Daraus ergibt sich aber auch, dass Array-Inhalte nicht verstreut im Speicher liegen können ("Hier ein bisschen, dann an anderer Stelle ein bisschen usw.").
Der große Nachteil ist, dass ein Array nicht wachsen kann. Man kann nur ein größeres anlegen und dann das kurze Array in das neue große reinkopieren. Für den Programmierer sieht es so aus, als würde das Array größer, aber in Wirklichkeit entsteht bloß ein neues Array, in das die alten Inhalte hinüberkopiert werden.
Wenn man eine Datenstruktur braucht, die mit ihren Anforderungen wächst, verwendet man eine verkettete Liste. Die hat den Vorteil, dass Verlängerungen (Einfüge-Operationen) problemlos, ganz schnell und ohne Umkopieren funktionieren. Ihr Nachteil ist, dass du nicht von vornherein weißt, wo das Element 1239 steht. Sondern du musst, wenn du einen bestimmten Index willst, die Liste vom Anfang an durchgehen, bis du beim gewünschten Element landest. Das heißt, das Wachsen geht super. Das Lesen ist, wenn man nicht vom Anfang an loslegen will, etwas langsamer.
Siehe zum Thema Listen:
Viele Grüße!
Sebastian _________________
Die gefährlichsten Familienclans | Opas Leistung muss sich wieder lohnen - für 6 bis 10 Generationen! |
|
Nach oben |
|
 |
Muecke Gast
|
Verfasst am: 07.01.2014, 01:56 Titel: |
|
|
OK es hat mir keine Ruhe gelassen
ich wollte das jetzt doch noch machen.
das Prog ist schon deutlich schneller geworden
interessant wäre es eine Leitzeit mal zu messen um zu sehen was man wirklich einspart
ich lasse jetzt das 2D Arrey auf der zweiten Ebene Prüfen ob es wirtlich Größer geworden ist oder nicht wenn nicht muss es auch nicht vergrößert werden.
so muss ich das nur noch von ziele zu ziele vergrößern
kann ich beim Datei Öffnen auch gleich auslesen wievielte Zeilen eine Datei hat? ohne sie durchlaufen zu müssen?
dann könnte ich das Arrey auch gleich auf die Zeilen von beginn an anpassen.
das mit dem Leerzeichen von Hand habe ich nicht ganz verstanden
das ist der Code jetzt:
Code: |
#include once "vbcompat.bi"
Declare Sub DateiInArrey (filename As String, Datei_Array() As String)
Dim As String filename
filename = "C:\Users\00\Desktop\Data\Dunlop001.csv"
filename = "C:\Users\00\Desktop\Data\replay_knete.csv"
SCREENRES 1024, 768, ,2
Locate 4, 10: Print "Datei:", filename
ReDim As String Daten3D(1)
DateiInArrey filename, Daten3D()
Sleep: End
Sub DateiInArrey (Datei As String, Datei_Array() As String)
Dim DNr As Integer = FreeFile
Dim As String Text, Separator
Dim As Integer x, i, n, anfptr, endptr
Separator = ","
Open Datei FOR INPUT ENCODING "ASCII" AS #DNr
n = 1
x = 0
DO
x + = 1
Locate 5, 10: Print "x:", x ' nur zu überprüfung vorhanden
ReDim Preserve Datei_Array(x, n) ' array vergrößern
LINE INPUT #DNr, Text
i = 0: anfptr = 1
Do ' string zerlegen
i + = 1 ' zähler erhöhen
If i>n Then
n=i
ReDim Preserve Datei_Array(x, n) ' array vergrößern
Locate 5, 33: Print "Hoechster Wert: ";n ' nur zu überprüfung vorhanden
EndIf
endptr = InStr(anfptr, Text, Separator) ' endpointer auf nächsten separator setzen
Datei_Array(x, i) = Trim(Mid(Text, anfptr, endptr - anfptr)) ' teilstring in array schreiben
anfptr = endptr + Len(Separator) ' anfangspointer hinter den separator setzen (anfang des nächsten teilstrings oder stringende)
Loop Until endptr = 0 ' weitermachen, bis der string zuende ist (wenn Seperator nict gefunden wierd wir 0 ausgegeben)
Loop until eof(1)
Close #DNr
End Sub
|
|
|
Nach oben |
|
 |
HorstD
Anmeldungsdatum: 01.11.2007 Beiträge: 110
|
Verfasst am: 07.01.2014, 03:21 Titel: |
|
|
Das mit dem Redim Preserve hast du anscheinend nicht verstanden.
Hier siehst du, was gemeint ist
http://www.vbarchiv.net/tipps/tipp_345-array-dynamisch-vergr-ern.html
Warum berechnest du jedesmal die Länge des Separators?
anfptr = endptr + Len(Separator)
Zitat: |
interessant wäre es eine Leitzeit mal zu messen um zu sehen was man wirklich einspart |
Code: | Dim zeit As Double
...
zeit = TIMER
...
...
Print "Zeit: ";str$(timer - zeit) |
|
|
Nach oben |
|
 |
grindstone
Anmeldungsdatum: 03.10.2010 Beiträge: 1279 Wohnort: Ruhrpott
|
Verfasst am: 07.01.2014, 08:27 Titel: |
|
|
@Muecke:
Muecke hat Folgendes geschrieben: | kann ich beim Datei Öffnen auch gleich auslesen wievielte Zeilen eine Datei hat? ohne sie durchlaufen zu müssen? | Nein, aber unter dem Geschwindigkeitsaspekt kann es sich durchaus lohnen, die Datei zweimal zu durchlaufen. Da der Inhalt der Datei beim ersten Lesen in den Hauptspeicher gecached wird, erfolgt der zweite Zugriff normalerweise deutlich schneller. Ob und inwieweit das in der Praxis funktioniert, hängt von der Dateigröße und der Größe deines Hauptspeichers ab.
@HorstD:
HorstD hat Folgendes geschrieben: | Warum berechnest du jedesmal die Länge des Separators?
anfptr = endptr + Len(Separator) | Das ist meine Schuld, ich habe ihm das als Beispielcode gepostet. In dem Programm, in dem die Sub bei mir werkelt, muß sie mit verschiedenen Separatorlängen zurechtkommen.
Zur Geschwindigkeitsoptimierung kann die Separatorlänge am Anfang der Sub in eine Variable geschrieben oder -wenn die Separatorlänge feststeht und sich auch niemals ändert- direkt durch die entsprechende Zahl ersetzt werden.
Eine dramatische Steigerung der Geschwindigkeit liesse sich durch die von nemored an anderer Stelle schon erwähnte Stringindizierung erreichen, allerdings ist hier die Programmierung etwas komplizierter als mit den vergleichsweise "idiotensicheren" Stringfunktionen. Jeder indizierte Stringzugriff außerhalb des gültigen Indexbereiches führt unausweichlich zu einem Programmabsturz.
Was ebenfalls viel Zeit kostet, ist die "Line Input"- Funktion. Hier ist es bei größeren Dateien von Vorteil, den Dateiinhalt in Portionen von 30000000 (in Worten: dreißig Millionen, das ist zumindest bei meinem System das Geschwindigkeitsoptimum) Zeichen in einen String einzulesen und anschließend diesen String zu bearbeiten. Aber das können wir besprechen, wenn du den Rest zum Laufen gebracht hast.
Übrigens habe ich festgestellt, daß wiederholtes "ReDim Preserve" bei mehrdimensionalen Arrays schnell zu Datensalat führt. Das Erweitern funktioniert offenbar nicht (immer) fehlerfrei.
Gruß
grindstone _________________ For ein halbes Jahr wuste ich nich mahl wie man Proggramira schreibt. Jetzt bin ich einen! |
|
Nach oben |
|
 |
Jojo alter Rang

Anmeldungsdatum: 12.02.2005 Beiträge: 9736 Wohnort: Neben der Festplatte
|
Verfasst am: 07.01.2014, 10:26 Titel: |
|
|
Muecke hat Folgendes geschrieben: | das mit dem Leerzeichen von Hand habe ich nicht ganz verstanden |
Naja, was Trim() tut, ist ja einfach links und rechts am String nach Leerzeichen zu suchen. Das kannst du auch selbst! Dabei einfach das erste Zeichen nach anfptr bzw letzte Zeichen vor endptr merken, das kein Leerzeichen ist, und dann die Positionen dieser Zeichen an Mid übergeben. _________________ » Die Mathematik wurde geschaffen, um Probleme zu lösen, die es nicht gäbe, wenn die Mathematik nicht erschaffen worden wäre.
 |
|
Nach oben |
|
 |
Muecke Gast
|
Verfasst am: 07.01.2014, 13:28 Titel: |
|
|
wenn ich das richtig verstanden habe wir hier ein Größeres Array erzeugt zu beginn wie man wahrscheinlich benötigt.
und am Schluss wenn man fertig ist, wird das Arrey auf die Tatsächliche Größe herunter geschrumpft
dadurch gehen dann auch keine Daten im Arrey verloren
HorstD hat Folgendes geschrieben: | Warum berechnest du jedesmal die Länge des Separators?
anfptr = endptr + Len(Separator) |
gute Frage da habe ich nicht dran gedacht
werde das gleich mal in eine Variable Packen. |
|
Nach oben |
|
 |
Muecke Gast
|
Verfasst am: 07.01.2014, 14:36 Titel: |
|
|
so jetzt lasse ich zu erst die Anzahl der Zeilen zählen und dann erstelle ich ein zu Großes Arrey:-) das ich später dann wider kleiner mache:-)
die Zeiten sind erstaunlich was es bringt.
habe es auch mal verglichen mit und Ohne Trim macht auch was aus.
werde das noch mit einer Kleinen abfrage machen (For Schleife) und die Leerzeichen am Anfang zu finden und am Ende, und schauen was dann an Zeiten raus kommt:
@grindstone: Das ist nicht Schuld, du hast mir nur ein Beispiel aufgezeigt was ich daraus gemacht habe ist meins nicht deins ich muss schon selbst das Hirn anschmeißen, will das ja selber hin bekommen *hoffe ich zumindest*
Das ist der Code bis jetzt
Code: | Declare Sub DateiInArrey (filename As String, Datei_Array() As String)
Dim As String filename, filepfad ' Hier kommt der Dateiname rein
filepfad = "C:\Users\00\Desktop\Data\"
' filename = "Dunlop001.csv"
filename = "replay_knete.csv"
' filename = "Test2.csv"
SCREENRES 1024, 768, ,2
Locate 1, 10: Print "Datei = "; filename
Dim zeit As Double ' #### Zeitmessung ####
zeit = Timer ' #### Zeitmessung Start ###
ReDim As String Daten3D(1)
DateiInArrey filepfad+filename, Daten3D()
Locate 7, 10: Print "Zeit: ";str$(timer - zeit)
Locate 30, 10: Print "Druecken sie eine Beliebige Taste zum schliessen...": Sleep: End
Sub DateiInArrey (Datei As String, Datei_Array() As String)
Dim DNr As Integer = FreeFile
Dim As Integer n, endptr, anfptr, i, LenSeparator, SpaltenAnzahl, ZeilenAnzahl
Dim As String Text, Separator
n = 0 ' n = die anzahl der Zeilen in der Datei
' --------------------------------------------------------------------------------------------
' Datei Öffnen und Zeilen Zählen
' --------------------------------------------------------------------------------------------
Open Datei FOR INPUT ENCODING "ASCII" AS #DNr
DO
LINE INPUT #DNr, Text: n + = 1
Loop until eof(1)
Close #DNr
ZeilenAnzahl = n
Locate 5, 10: Print "Zeilen anzahl = ";ZeilenAnzahl
' --------------------------------------------------------------------------------------------
' 2-D Arrey erstellen in der Größe von (Zeilen aus der Datei, 1)
' --------------------------------------------------------------------------------------------
ReDim Preserve Datei_Array(ZeilenAnzahl, 5000) ' array erstellen
' --------------------------------------------------------------------------------------------
' Datei Öffnen Zeilen Lesen und zerlegen dann Speichern
' --------------------------------------------------------------------------------------------
Open Datei FOR INPUT ENCODING "ASCII" AS #DNr
Separator = ",": LenSeparator = Len(Separator)
n = 0
SpaltenAnzahl = 0
DO
LINE INPUT #DNr, Text: n + = 1
i = 0
Do
i + = 1
If i > SpaltenAnzahl Then SpaltenAnzahl = i ' ermitteln des Höchsten Spaltenwertes
endptr = InStr(anfptr, Text, Separator) ' endpointer auf nächsten separator setzen
Datei_Array(n, i) = Trim(Mid(Text, anfptr, endptr - anfptr)) ' teilstring in array schreiben
anfptr = endptr + LenSeparator ' anfangspointer hinter den separator setzen (anfang des nächsten teilstrings oder stringende)
Loop Until endptr = 0 ' weitermachen, bis der string zuende ist (wenn Seperator nict gefunden wierd wir 0 ausgegeben)
Loop until eof(1)
Close #DNr
' --------------------------------------------------------------------------------------------
' 2-D Arrey auf die tatsächliche Größe erstellen
' --------------------------------------------------------------------------------------------
ReDim Preserve Datei_Array(ZeilenAnzahl, SpaltenAnzahl)
Locate 6, 10: Print "Spalten anzahl = ";SpaltenAnzahl
End Sub
|
|
|
Nach oben |
|
 |
grindstone
Anmeldungsdatum: 03.10.2010 Beiträge: 1279 Wohnort: Ruhrpott
|
Verfasst am: 08.01.2014, 03:46 Titel: |
|
|
@Muecke:
Du brauchst die Datei zum zweiten Durchsuchen nicht zu schließen und erneut zu öffnen. Es reicht, vor dem zweiten Durchgang den Dateizeiger mitauf den Anfang der Datei zu setzen.
Statt n kannst du im ersten Durchgang direkt die Variable "ZeilenAnzahl" hochzählen, dann sparst du dir das Umkopieren.
Beim Anlegen des Arrays ist das Schlüsselwort "Preserve" überflüssig. Du willst ja keine vorherigen Werte erhalten.
Falls der gesamte Inhalt der Datei in den Arbeitsspeicher passt (was hier ja offensichtlich der Fall ist, denn sie wird ja komplett in ein Array kopiert), ist vom Gesichtspunkt der Geschwindigkeit ein anderes Vorgehen sinnvoller: Zuerst wird die gesamte Datei als ein einziger String ausgelesen. Dann wird dieser String zweimal durchsucht, einmal auf Kennzeichen für Zeilenende ("Chr(13,10)") und ein zweitesmal auf den Separator (","). Die Positionen dieser Trennzeichen innerhalb des Strings (Pointer auf den String) werden in jeweils einem (Integer-)Array gespeichert. Mithilfe dieser Pointer ist ein gezielter Zugriff auf jeden Dateieintrag genausogut möglich wie mit einem Textarray, und man spart sich das gesamte Umkopieren.
Mir fehlt im Augenblick die Zeit, ein entsprechendes Programm zu schreiben, ich werde versuchen, es in den nächsten Tagen nachzuliefern.
Gruß
grindstone _________________ For ein halbes Jahr wuste ich nich mahl wie man Proggramira schreibt. Jetzt bin ich einen! |
|
Nach oben |
|
 |
grindstone
Anmeldungsdatum: 03.10.2010 Beiträge: 1279 Wohnort: Ruhrpott
|
Verfasst am: 09.01.2014, 00:47 Titel: |
|
|
@Mücke: Hier das versprochene Beispielprogramm. Code: | Dim As String filename, filepfad, dateiinhalt
Dim As Integer x, zeilenindex, eintragsindex
Dim As Double zeitmerken
ReDim As Integer eintragspointer(1), zeilenanfangsindex(1)
Declare Function EintragHolen(zeile As Integer, spalte As Integer, ByRef dateiinhalt As String, _
eintragspointer() As Integer, zeilenanfangsindex() As Integer) As String
zeitmerken = Timer
filepfad = "C:\Users\00\Desktop\Data\"
filename = "replay_knete.csv"
''erzeugen einer testdatei
'filename = "test.txt"
'Open filepfad + filename For Output As #1
'Print #1, "z1 e1, z1 e2, z1 e3"
'Print #1, "z2 e1, z2 e2, z2 e3"
'Print #1, "z3 e1, z3 e2, z3 e3, z3 e4x"
'Close 1
'datei als string in den hauptspeicher einlesen
Open filepfad + filename For Binary Access Read As #1
dateiinhalt = Input(Lof(1),#1)
Close 1
ReDim eintragspointer(Len(dateiinhalt) / 2) 'maximal zu erwartende pointeranzahl
ReDim zeilenanfangsindex(Len(dateiinhalt) / 2)
eintragsindex = 1
eintragspointer(1) = 0 'ersten pointer auf anfang der datei setzen...
zeilenindex = 1
zeilenanfangsindex(1) = 1 '...und als ersten zeilenindex speichern
'separatoren suchen und merken
For x = 0 To Len(dateiinhalt) - 1 'die pointer bei stringindizierung beginnen bei 0
Select Case dateiinhalt[x] 'ascii - wert des zeichens an stringposition x
Case 13 'jede zeile wird mit einem Chr(13,10) abgeschlossen
zeilenindex += 1
eintragsindex += 1
eintragspointer(eintragsindex) = x + 2 'pointer auf anfang des nächsten eintrags setzen
zeilenanfangsindex(zeilenindex) = eintragsindex 'index des eintragspointers merken als beginn von zeile <zeilenindex>
Case Asc(",") 'ascii - wert des separators
eintragsindex += 1
eintragspointer(eintragsindex) = x + 1 'pointer auf anfang des nächsten eintrags setzen
End Select
Next
'arrays auf das korrekte maß verkleinern
ReDim Preserve zeilenanfangsindex(zeilenindex)
ReDim Preserve eintragspointer(eintragsindex)
Print EintragHolen(3,4,dateiinhalt,eintragspointer(),zeilenanfangsindex())
Print
Print "Rechenzeit "; Timer - zeitmerken; " Sekunden"
Sleep
End
Function EintragHolen(zeile As Integer, spalte As Integer, ByRef dateiinhalt As String, _
eintragspointer() As Integer, zeilenanfangsindex() As Integer) As String
Dim As String text
Dim As Integer anfptr, endptr, textlen, x
If (zeile > UBound(zeilenanfangsindex) - 1) Or (zeile < 1) Or (spalte < 1) Then 'falsche zeilen- oder spaltenangabe
Return "" 'fehler --> leerstring als rückgabewert
EndIf
anfptr = eintragspointer(zeilenanfangsindex(zeile) + (spalte - 1)) 'pointer auf das erste zeichen des gesuchten eintrags
endptr = eintragspointer(zeilenanfangsindex(zeile) + spalte) - 2 'pointer auf das letzte zeichen des gesuchten eintrags
If zeilenanfangsindex(zeile + 1) = zeilenanfangsindex(zeile) + spalte Then 'der gesuchte eintrag ist der letzte in der zeile
endptr -= 1 'endpointer korrigieren
ElseIf zeilenanfangsindex(zeile + 1) < zeilenanfangsindex(zeile) + spalte Then 'der angesprochene eintrag gehört schon zur nächsten zeile
Return "" 'fehler --> spaltennummer zu groß --> leerstring als rückgabewert
EndIf
textlen = endptr - anfptr + 1 'länge des rückgabestrings berechnen
text = String(textlen,Chr(0)) 'leerstring mit der erforderlichen länge erzeugen
For x = 0 To textlen - 1 'eintrag in rückgabestring kopieren
text[x] = dateiinhalt[anfptr + x]
Next
Return text
End Function | Ich hoffe, die Kommentare im Programm und die Verwendung von möglichst sprechenden Variablennamen reichen aus, um die Funktionsweise zu erklären, besonders die doppelte Indizierung der Zeilenanfänge. Der auskommentierte Block am Anfang dient zum Erzeugen einer kleinen Testdatei, ich habe ihn mal dringelassen.
Die Arbeitsgeschwindigkeit des Programms dürfte kaum noch zu übertreffen sein, allerdings etwas auf Kosten der Flexibilität:- Die Separatoren sind auf "Chr(13,10)" und "," festgelegt (kann bei Bedarf natürlich geändert werden)
- Der gesamte Dateiinhalt und die am Anfang riesengroßen Arrays (sie haben zusammen etwa die achtfache Größe der Datei) müssen gleichzeitig in den Arbeitsspeicher passen
- Diese Methode ist nur zum Lesen geeignet. Wenn die Einträge verändert und wieder abgespeichert werden sollen, ist es doch sinnvoller, vorher alles in ein Array zu kopieren
Gruß
grindstone _________________ For ein halbes Jahr wuste ich nich mahl wie man Proggramira schreibt. Jetzt bin ich einen! |
|
Nach oben |
|
 |
|
|
Du kannst keine Beiträge in dieses Forum schreiben. Du kannst auf Beiträge in diesem Forum nicht antworten. Du kannst deine Beiträge in diesem Forum nicht bearbeiten. Du kannst deine Beiträge in diesem Forum nicht löschen. Du kannst an Umfragen in diesem Forum nicht mitmachen.
|
|