Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht Das deutsche QBasic- und FreeBASIC-Forum
Für euch erreichbar unter qb-forum.de, fb-forum.de und freebasic-forum.de!
 
FAQFAQ   SuchenSuchen   MitgliederlisteMitgliederliste   BenutzergruppenBenutzergruppen  RegistrierenRegistrieren
ProfilProfil   Einloggen, um private Nachrichten zu lesenEinloggen, um private Nachrichten zu lesen   LoginLogin
Zur Begleitseite des Forums / Chat / Impressum
Aktueller Forenpartner:

Open Datei auslesen erst ab Zeile i

 
Neues Thema eröffnen   Neue Antwort erstellen    Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht -> Allgemeine Fragen zu FreeBASIC.
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen  
Autor Nachricht
Muecke
Gast





BeitragVerfasst am: 06.01.2014, 16:19    Titel: Open Datei auslesen erst ab Zeile i Antworten mit Zitat

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

BeitragVerfasst am: 06.01.2014, 16:35    Titel: Antworten mit Zitat

Einfacher als das geht's wirklich nicht, und Performance wird auch nicht dein Problem sein, oder? zwinkern
_________________
» Die Mathematik wurde geschaffen, um Probleme zu lösen, die es nicht gäbe, wenn die Mathematik nicht erschaffen worden wäre.
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden Website dieses Benutzers besuchen
Muecke
Gast





BeitragVerfasst am: 06.01.2014, 16:40    Titel: Antworten mit Zitat

du hast recht die Performance ist nicht wirklich mein Problem lächeln.

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 lächeln perfekt.
Nach oben
grindstone



Anmeldungsdatum: 03.10.2010
Beiträge: 1279
Wohnort: Ruhrpott

BeitragVerfasst am: 06.01.2014, 18:13    Titel: Antworten mit Zitat

@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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
Muecke
Gast





BeitragVerfasst am: 07.01.2014, 00:02    Titel: Antworten mit Zitat

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 traurig

nur das teil ist extrem langsam traurig

meine Datei:
Zeilen: 360
Spalten: 1011
Größe 1.370 KB

das Teil ist wirklich extrem langsam traurig

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

BeitragVerfasst am: 07.01.2014, 00:27    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden Website dieses Benutzers besuchen
Muecke
Gast





BeitragVerfasst am: 07.01.2014, 00:42    Titel: Antworten mit Zitat

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 traurig

bei der Ausgabe ist mir dann aufgefallen das es die Verarbeitens Dauer ist, und nicht das Prog abgestürzt ist lächeln

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

BeitragVerfasst am: 07.01.2014, 01:25    Titel: Verkettete Liste statt Array und REDIM PRESERVE benutzen Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden Website dieses Benutzers besuchen
Muecke
Gast





BeitragVerfasst am: 07.01.2014, 01:56    Titel: Antworten mit Zitat

OK es hat mir keine Ruhe gelassen lächeln
ich wollte das jetzt doch noch machen.

das Prog ist schon deutlich schneller geworden lächeln
interessant wäre es eine Leitzeit mal zu messen um zu sehen was man wirklich einspart lächeln

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 lächeln

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 traurig


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

BeitragVerfasst am: 07.01.2014, 03:21    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
grindstone



Anmeldungsdatum: 03.10.2010
Beiträge: 1279
Wohnort: Ruhrpott

BeitragVerfasst am: 07.01.2014, 08:27    Titel: Antworten mit Zitat

@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. lächeln

Ü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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
Jojo
alter Rang


Anmeldungsdatum: 12.02.2005
Beiträge: 9736
Wohnort: Neben der Festplatte

BeitragVerfasst am: 07.01.2014, 10:26    Titel: Antworten mit Zitat

Muecke hat Folgendes geschrieben:
das mit dem Leerzeichen von Hand habe ich nicht ganz verstanden traurig

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
Benutzer-Profile anzeigen Private Nachricht senden Website dieses Benutzers besuchen
Muecke
Gast





BeitragVerfasst am: 07.01.2014, 13:28    Titel: Antworten mit Zitat

HorstD hat Folgendes geschrieben:
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


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 lächeln
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 zwinkern
werde das gleich mal in eine Variable Packen.
Nach oben
Muecke
Gast





BeitragVerfasst am: 07.01.2014, 14:36    Titel: Antworten mit Zitat

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 zwinkern ich muss schon selbst das Hirn anschmeißen, will das ja selber hin bekommen zwinkern *hoffe ich zumindest*

Das ist der Code bis jetzt lächeln



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

BeitragVerfasst am: 08.01.2014, 03:46    Titel: Antworten mit Zitat

@Muecke:
Du brauchst die Datei zum zweiten Durchsuchen nicht zu schließen und erneut zu öffnen. Es reicht, vor dem zweiten Durchgang den Dateizeiger mit
Code:
Seek DNr,1
auf 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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
grindstone



Anmeldungsdatum: 03.10.2010
Beiträge: 1279
Wohnort: Ruhrpott

BeitragVerfasst am: 09.01.2014, 00:47    Titel: Antworten mit Zitat

@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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
Beiträge der letzten Zeit anzeigen:   
Neues Thema eröffnen   Neue Antwort erstellen    Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht -> Allgemeine Fragen zu FreeBASIC. Alle Zeiten sind GMT + 1 Stunde
Seite 1 von 1

 
Gehe zu:  
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.

 Impressum :: Datenschutz