DATANORM V4/V5 Artikel - Textfile

Zugriff, Engines, Konvertierung. Von ADS über DBF bis zu SQL.

Moderator: Moderatoren

Antworten
xbaseklaus
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 246
Registriert: Mi, 04. Jun 2014 12:01
Wohnort: FRANKEN

DATANORM V4/V5 Artikel - Textfile

Beitrag von xbaseklaus »

Guten Morgen ,

ich bins wieder mal .

Habe hier eine etwa 60MB große DATANORM Datei welche in das Programm eingelesen wird welches ich hier von Clipper auf Xbase Fenstermodus :-) umschreibe.

Es wird folgendermaßen in die aktuelle Artikel DBF Datenbank eingelesen:

File wird Zeile für Zeile eingelesen (zerlegt ) und in eine DBF Datenbank zwischengespeichert.

Danach wird die zwischengespeicherte Datanorm . DBF mit dem vorhanden Artikelstamm abgeglichen -> Artikel neu anlegen bzw aktualisieren !

--- nur der Einlesevorgang dauert jetzt schon ne STUNDE ... :-(

Außerdem habe ich dazu eine DATANORM.RAB ( Rabatt Datei ) welche anscheinend neu ist bzw noch nie berücksichtigt worden ist #-o :oops: :?: :!: :roll:


Gibt es da schon was fertiges wo ich nur meine Datanbank Felder zuweisen muß oder so .... ?

Mit Array sollte es wohl schneller gehen , aber ich lese hier ständig was von SPEICHERÜBERLAUF , der natürlich nicht erwünscht ist ...

Hat jemand Erfahrung damit bzw kann mich jemand in die richtige Richtung treten :idea: 8)

Für Denkanstöße wäre ich dankbar !

mfg Klaus
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15689
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: DATANORM V4/V5 Artikel - Textfile

Beitrag von brandelh »

Das zeilenweise Einlesen ist problematisch, die 60 MB kann man leicht auf einen Schlag in ein Array laden,
Eine Zeile ergibt dann ein Array-Element. Es könnte problematisch werden, wenn die Anzahl der Zeilen sehr hoch ist.

http://www.xbaseforum.de/viewtopic.php?f=16&t=4268

Es macht aber eventuell auch Sinn das gleich aus der eingelesenen Datei zu machen.

Hast du mehr Infos zum Aufbau ?

Im obigen Beispiel kannst du sehen wie die Datei auf einen Schlag eingelesen wird (in eine Variable, das braucht keine Sekunde bei 50 MB).
Aus dieser Variable werden dann die VON / BIS Werte für eine Zeile berechnet, der String selbst aber nicht geändert.

Diese VON BIS Werte könntest du nutzen um die Daten einer Zeile zu erhalten und per Programm direkt in deine Anwendungsdaten einpflegen.
Das macht aber natürlich nur Sinn, wenn das einmalig geschieht. Wenn DU einmalig Update-DBFs einliest und dann versendest, dann nicht.

100.000 eindimensionale Arrayelemente können Problematisch sein, bei mehr Dimmensionen und Strings geht es vorher in die Knie.
500 MB im RAM sind kein Problem wenn man ab 2 GB Hauptspeicher hat UND der String nur gelesen nicht aber geändert wird !
Gruß
Hubert
xbaseklaus
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 246
Registriert: Mi, 04. Jun 2014 12:01
Wohnort: FRANKEN

Re: DATANORM V4/V5 Artikel - Textfile

Beitrag von xbaseklaus »

Code: Alles auswählen

A;N;10000002;00;Westaflex Grobfilter WAC G4;o Sommerbypassklappe, f 250WACCF, 2 Stck;1;0;ST;2037;6630;231;;
B;N;10000002;000CFFILT002;000CFFILT002; ;0;0;0;4024875118659; ;231903;0;1; ; ;
A;N;10000003;00;Westaflex Grobfilter WAC G4;m Sommerbypassklappe, f 250WACCF, 2 Stck;1;0;ST;2185;6630;231;;
B;N;10000003;000CFFILT004;000CFFILT004; ;0;0;0;4024875122441; ;231903;0;1; ; ;
A;N;10000004;00;Westaflex 3-Stufenschalter;fr externen Anschluss an 140W;1;0;ST;8272;6630;231;;
B;N;10000004;000CFSBED;000CFSBED; ;0;0;0;2000003259564; ;231903;0;1; ; ;
z.B. 3 Artikel schauen so aus ... hoffe ich darf das Posten ...!

mfg
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15689
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: DATANORM V4/V5 Artikel - Textfile

Beitrag von brandelh »

wieviele Zeilen hat die Datei in etwa ?

da es eine CSV ist könnte eventuell ein Einlesen über Excel 2007ff. vorteilhaft sein, ein ActiveX Beispiel zum Einlesen von DBF Dateien von Alaska kann man dann abändern.
Für die Zeilen und Zellen weise Übertragung wäre dann aber die ActiveX Variante zu langsam, hier könnte LibXL helfen.

Ansonsten geht auch direktes Übertragen wie oben beschrieben alles in eine Datei oder in 4 KB Blöcken einlesen,
Zeilen ermitteln und Zeilenweise aus dem String lesen. Direkt verarbeiten ... Pablo hat eine Token-Funktion die man leicht nutzen kann, um die Einzelnen Felder aus der Zeile auszulesen.
Gruß
Hubert
Benutzeravatar
Werner_Bayern
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2120
Registriert: Sa, 30. Jan 2010 22:58
Wohnort: Niederbayern
Hat sich bedankt: 29 Mal
Danksagung erhalten: 70 Mal

Re: DATANORM V4/V5 Artikel - Textfile

Beitrag von Werner_Bayern »

Das sind A und B Sätze für Artikel, das N steht für Neuanlage. Rabattsätze enthalten Rabatte auf Warengruppen. Beschreibung dazu gibts nur von einem Verlag, sonst nirgends. Musst Dir das Buch kaufen, suche nach Datanorm 5 Satzaufbau. Ist aber inzwischen ein sehr altes, überholtes Format.
es grüßt

Werner

<when the music is over, turn off the lights!>
xbaseklaus
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 246
Registriert: Mi, 04. Jun 2014 12:01
Wohnort: FRANKEN

Re: DATANORM V4/V5 Artikel - Textfile

Beitrag von xbaseklaus »

Werner_Bayern hat geschrieben:Das sind A und B Sätze für Artikel, das N steht für Neuanlage. Rabattsätze enthalten Rabatte auf Warengruppen. Beschreibung dazu gibts nur von einem Verlag, sonst nirgends. Musst Dir das Buch kaufen, suche nach Datanorm 5 Satzaufbau. Ist aber inzwischen ein sehr altes, überholtes Format.
Die Zuteilung hab ich mir mittlerweile aus dem Internet zusammengesucht - das ist noch DATANORM V4

Soweit scheint er auch alles einzulesen, das Problem ist nur er ließt die Daten schon seit 9.30 Uhr ein :-(

werde dann doch das ganze mal in ein array einlesen !

mfg Klaus
Benutzeravatar
Koverhage
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2470
Registriert: Fr, 23. Dez 2005 8:00
Wohnort: Aalen
Hat sich bedankt: 102 Mal
Danksagung erhalten: 3 Mal
Kontaktdaten:

Re: DATANORM V4/V5 Artikel - Textfile

Beitrag von Koverhage »

Ich würde mal die Einleseroutine posten, denn je nach dem wie Du das in ein Array einliest, braucht es auch so lange.
Gruß
Klaus
xbaseklaus
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 246
Registriert: Mi, 04. Jun 2014 12:01
Wohnort: FRANKEN

Re: DATANORM V4/V5 Artikel - Textfile

Beitrag von xbaseklaus »

Es sind ca 380000 Artikel

Das einlesen sieht so aus

Code: Alles auswählen

select 44
use koe
zap


test:= filestr("datanorm.001")
clear
? "Datenbank wird umgewandelt !"

wait("Weiter mit RETURN !!!")

x=numat("A;N;",test)

a=1

do while .not. a > x

   z=atnum("A;N",test,a)

   y=atnum("B;N",test,a)

   test2:= filestr("datanorm.001",500,z)

   test3=beforatnum(";",test2,9,0)
   test5=beforatnum(";",test2,10,0)
   t1= token(test2,";",2,0)
   t2= token(test2,";",4,0)
   t3= token(test2,";",5,0)
   t4= token(test3,";")
   t5= token(test5,";")
   t5u=val(t5)/100

   test2:= filestr("datanorm.001",500,y)
   test8=beforatnum(";",test2,4,0)
   test9=beforatnum(";",test2,10,0)

   t8= token(test8,";")
   t9= token(test9,";")


   append blank
   replace artikelnr with t1
   replace produkt with t2
   replace produkt2 with t3
   replace Einheit with t4
   replace vkpreis with t5u
   replace matchcode with t8
   replace ean with t9
   a=a+1
enddo
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15689
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: DATANORM V4/V5 Artikel - Textfile

Beitrag von brandelh »

Versuche doch einfach mal die Daten mit aBuffer := TxtFile2Array(cDateiName,cConvert) (LINK oben) einzulesen, mal sehen ob das geht und wie lange es dauert.
Allerdings werden 380.000 Arrays wohl nicht wirklich schnell sein und eventuell auch die Grenzen sprengen.

Ich wundere mich oben warum du mehrfach die Datei einliest, vermutlich in Teilen, aber viel zu kleinen.
Bei akutellen Rechnern und Xbase++ sollte der buffer 4096 Zeichen groß sein (4 KByte Block).
Gruß
Hubert
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15689
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: DATANORM V4/V5 Artikel - Textfile

Beitrag von brandelh »

verbessern kann man auch die Einlesung selbst: nicht einzelne Zugriffe für jeden Token, sondern Pablos Funktion nutzen:

http://www.xbwin.com/ot4xbXHlp/

Tokenize( cZeile , ";" ) -> aTokens // ";" kann man auch weg lassen, da es Standard ist.
Split a string based on a given delimiter and return an array with the tokens.

Syntax:

Parameters:
<cStr>
String containing the tokens to extract.
<cDelim>
String with the delimiter characters. ";" by default.
Return Value:
Array with the extracted tokens.
Diese Funktion gibt mit einem Aufruf eine Textzeile als Array zurück, vor der Schleife wird ermittelt oder festgelegt welches Arrayelement zu welchem Feld gehört und alles geht schneller.
Gruß
Hubert
Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 12903
Registriert: Do, 16. Mär 2006 7:55
Wohnort: Hamburg
Hat sich bedankt: 19 Mal
Danksagung erhalten: 44 Mal

Re: DATANORM V4/V5 Artikel - Textfile

Beitrag von AUGE_OHR »

xbaseklaus hat geschrieben:z.B. 3 Artikel schauen so aus ...
Frage : wird eine Zeile mit CHR(13) und/oder CHR(10) abgeschlossen ?
Ist am Ende der Datei ein CHR(26) ? CRTL-Z

unter Xbase++ hast du nicht das 64Kb Limit d.h. du könntest > 1GB im Stück per Memoread() ( statt mehrfach FileStr() ) in wenigen Sekunden in Speicher einlesen !
... und in eine DBF Datenbank zwischengespeichert.
kannst du das nicht Satzweise gleich verarbeiten ?

das APPEND / REPLACE nimmt viel mehr Zeit ein als die String Verarbeitung und 60 MB ist nun wirklich nicht viel.
gruss by OHR
Jimmy
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15689
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: DATANORM V4/V5 Artikel - Textfile

Beitrag von brandelh »

So ich habe mal eine entsprechende Testdatei zusammengebaut und ein Importprogramm geschrieben.

Mein Testprogramm mit meiner Funktion TxtFile2Array(cDateiName,cConvert)
in Verbindung mit Pablos Tokenize( cZeile , ";" ) und einer EXCLUSIV geöffneten DBF liest das in
einer Minute (AMD 4 Kern mit 3 Ghz, 8 GB RAM und 2,5" Festplatte) oder
sogar 30 Sekunden (INTEL i5-4670, 16 GB RAM und SSD) ein.

Das wichtigste bei solchen Aktionen ist

1. EXCLUSIVER Zugriff auf die DBF, OHNE Index (den baut man später auf wenn man diesen braucht).
2. Im RAM den großen String möglichst nur lesen
3. Textdateien auf einmal in den RAM, oder zumindest in 4 KB Blocks die nur gelesen werden.

Die 755.000 Zeilen hat meine Routine übrigens ohne murren und mit Standard STACK Einstellung eingelesen.
Wenn es deutlich mehr werden, muss man mit der 4 KB Block Methode arbeiten.

Das wäre doch mal wieder eine Klasse wert ... :wink:
Gruß
Hubert
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15689
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: DATANORM V4/V5 Artikel - Textfile

Beitrag von brandelh »

brandelh hat geschrieben:Das wäre doch mal wieder eine Klasse wert ... :wink:
und die habe ich hier sogar schon im Einsatz ... schneller als die Array Version :!:

I5-2500, 8 GB Ram, 3,5" Festplatte:

Array-Version 47 Sekunden (mit Pablos Tokenize(), mein eigener dauert 30 Sekunden länger)
TextRead-Version 30 Sekunden und hier kann man einfach eine Verlaufsanzeige einbauen.

Array-Version:

Code: Alles auswählen

#include "Gra.ch"
#include "Xbp.ch"
#include "Common.ch"
#include "Inkey.ch"
#include "ot4xb.ch"

#ifndef CRLF
   #define CRLF chr(13)+chr(10)
#endif

procedure main()
   local aBuffer, cFile, aZeile, x, nAnzA, nAnzB, nAnzU, nDauer
   field artikelnr, produkt, produkt2, Einheit, vkpreis, matchcode, ean

   CLS

   SET EXCLUSIVE ON // bei solchen Operationen ist dies 10x schneller !

   set alternate to HB-Array.TXT
   set alternate on

   ? time(),"Start"

   nDauer  := seconds()

   cFile   := "DATANORM.001"

   use KOE EXCLUSIVE
   if neterr()
      ? "KOE.DBF kann nicht geöffnet werden"
      quit
   endif
   ZAP

   aBuffer := TxtFile2Array(cFile)

   ? time(),"DATANORM in aBuffer Array eingelesen, Anzahl Zeilen: ",len(aBuffer)

   ? time(),"Version:",aBuffer[1]

   nAnzA :=   0
   nAnzB :=   0
   nAnzU :=   0

   for x := 2 to len(aBuffer) // Daten
       if x = 2 .or. int(x/100) == x/100
          ? time(),"Zeile:",x,aBuffer[x]
       endif

       aZeile := Tokenize(aBuffer[x]) // ot4xb.dll einbinden !
       * aZeile := Txt2Array(aBuffer[x],";")

       do case
          case aZeile[1]="A" // scheinbar sind A und B Sätze zu kombinieren !
               nAnzA++
               append blank
               replace artikelnr with aZeile[3] // diese Zuordnung prüfen !
               replace produkt   with aZeile[5]
               replace produkt2  with aZeile[6]
               replace Einheit   with aZeile[9]
               replace vkpreis   with val(aZeile[10]) / 100 // scheinbar werden die Beträge in CENT angegeben.
          case aZeile[1]="B" // scheinbar sind A und B Sätze zu kombinieren !
               nAnzB++
               if artikelnr # aZeile[3]
                  ? "B Satz passt nicht zu A Satz !"
               endif
               replace matchcode with aZeile[4]
               replace ean       with aZeile[10]
          otherwise
               nAnzU++
               ? x,"Unbekannte Satzart:",aZeile[1]," von ",left(aBuffer[x],10)
       end
   next
   ? time(),"Ende "
   nDauer  := seconds()-nDauer
   ?
   ? "Anzahl Zeilen (ohne Titel): ",len(aBuffer)-1
   ? "Anzahl A-Sätze:             ",nAnzA
   ? "Anzahl B-Sätze:             ",nAnzB
   ? "Anzahl unplausibler Sätze:  ",nAnzU
   ?
   ? "Anzahl Datensätze in DBF:   ",Recc()
   ?
   ? "Laufzeit                    ",nDauer,"Sekunden"
   ?
   inkey(20)

return








*-------------------------------------------------------------------------------
FUNCTION TxtFile2Array(cDateiName,cConvert) // TXT einlesen und zeilenweise in Array einlesen
                                            // cConvert: NIL / empty - nichts tun
                                            //           O2A         - Oem 2 Ansi
                                            //           A2O         - Ansi 2 Oem
   local cDateiInhalt, aZeilen := {}, nVon, nBis, cTxt, nMaxLen
   DEFAULT cConvert to ""
   nVon := 1
   nBis := 0
   if file(cDateiName)
      cDateiInhalt := memoread(cDateiName)
      if ! empty(cDateiInhalt)
         cConvert := upper(alltrim(cConvert))
         do case
            case cConvert == "O2A"
                 cDateiInhalt := ConvToAnsiCP(cDateiInhalt)
            case cConvert == "A2O"
                 cDateiInhalt := ConvToOemCP(cDateiInhalt)
         endcase
         nMaxLen := len(cDateiInhalt) // nur einmal aufrufen
         do while .t.
            nBis := at(CRLF,cDateiInhalt,nVon)
            if nBis = 0    // Ende der Datei wurde erreicht, letzten Satz nicht vergessen
               cTxt := substr(cDateiInhalt,nVon)
               nVon := len(cDateiInhalt)+1
            else
               cTxt := substr(cDateiInhalt,nVon,nBis-nVon)
               nVon := nBis+2
            endif
            cTxt := strTran(cTxt,CRLF,"")                // entferne die CRLF am Zeilenende
            aadd(aZeilen,cTxt)
            if nVon > nMaxLen
               exit
            endif
         enddo
         cDateiInhalt := NIL // sofort als unnötig markieren.
      endif
   endif
return aZeilen

*-------------------------------------------------------------------------------
FUNCTION Txt2Array(cDateiInhalt,cDelim)
   local aZeilen := {}, nVon, nBis, cTxt
   DEFAULT cDelim TO CRLF
   nVon := 1
   nBis := 0
   if ! empty(cDateiInhalt)
      do while .t.
         nBis := at(cDelim,cDateiInhalt,nVon)
         if nBis = 0    // Ende der Datei wurde erreicht, letzten Satz nicht vergessen
            cTxt := substr(cDateiInhalt,nVon)
            nVon := len(cDateiInhalt)+1
         else
            cTxt := substr(cDateiInhalt,nVon,nBis-nVon)
            nVon := nBis+len(cDelim)
         endif
         cTxt := strTran(cTxt,CRLF,"")                // entferne die CRLF am Zeilenende
         aadd(aZeilen,cTxt)
         if nVon > len(cDateiInhalt)
            exit
         endif
      enddo
   endif
return aZeilen
TextReader()

Code: Alles auswählen

#include "ot4xb.ch"

#ifndef CRLF
   #define CRLF chr(13)+chr(10)
#endif


* Klasse zum sequentiellen Einlesen groáer Dateien

#IF .t.

procedure main
   local nZeile := 0
   local cZeile := ""
   local nSize, nBytes := 0
   local nDauer
   local oTR, aZeile, x, nAnzA, nAnzB, nAnzU
   field artikelnr, produkt, produkt2, Einheit, vkpreis, matchcode, ean
   cls

   use KOE EXCLUSIVE
   if neterr()
      ? "KOE.DBF kann nicht geöffnet werden"
      quit
   endif
   ZAP


   oTR := TextReader( "DATANORM.001" ) // per Funktion

   * oTR := TxtReader():new( "DATANORM.001" ) // oder Klassentypisch !

   if oTR:FError() <> 0
      @ 1,1 say "Fehler beim Datei ”ffnen: "+oTR:ErrMsg()
      quit
   endif
   nSize := oTR:FSize()
   @ 1,1 say  "Testdatei sequentiell einlesen"
   @ 3,1 say  "DateigrӇe      "+transform(nSize,"###,###,###,###,###")
   @ 4,1 say  "Version:        "+oTR:GetLine() // 1. Zeile hat Versionsinfos

   nDauer := seconds()

   nAnzA :=   0
   nAnzB :=   0
   nAnzU :=   0

   do while ! oTR:EOF()
      cZeile := oTR:GetLine()  // aktuelle Zeile auslesen, interner Zeiger auf nächste Zeile setzen.

*      if len(cZeile)=0
*         exit
*      endif
      nZeile++
      nBytes += len(cZeile)+2 // CR+LF = 2 Byte

      aZeile := Tokenize(cZeile) // ot4xb.dll einbinden !

      do case
         case aZeile[1]="A" // scheinbar sind A und B Sätze zu kombinieren !
              nAnzA++
              append blank
              replace artikelnr with aZeile[3] // diese Zuordnung prüfen !
              replace produkt   with aZeile[5]
              replace produkt2  with aZeile[6]
              replace Einheit   with aZeile[9]
              replace vkpreis   with val(aZeile[10]) / 100 // scheinbar werden die Beträge in CENT angegeben.
         case aZeile[1]="B" // scheinbar sind A und B Sätze zu kombinieren !
              nAnzB++
              if artikelnr # aZeile[3]
                 @20,1 say str(nZeile)+"  B Satz passt nicht zu A Satz !"
              endif
              replace matchcode with aZeile[4]
              replace ean       with aZeile[10]
         otherwise
              nAnzU++
              @21,1 say str(nZeile)+"  Unbekannte Satzart: '"+aZeile[1]+"' von "+left(cZeile,10)
      end

      if nZeile % 100 = 0    //
         @ 5,1 say  "gelesene Bytes    "+transform(nBytes,"###,###,###,###,###")
         @ 6,1 say  "gelesene Zeilen   "+transform(nZeile,"###,###,###,###,###")
         @ 7,1 say  "gelesene A-Zeilen "+transform(nAnzA, "###,###,###,###,###")
         @ 8,1 say  "gelesene B-Zeilen "+transform(nAnzB, "###,###,###,###,###")
         @ 9,1 say  "gelesene U-Zeilen "+transform(nAnzU, "###,###,###,###,###")
      endif

   enddo

   oTR:Destroy()

   nDauer := seconds() - nDauer

   @ 5,1 say  "gelesene Bytes    "+transform(nBytes,"###,###,###,###,###")
   @ 6,1 say  "gelesene Zeilen   "+transform(nZeile,"###,###,###,###,###")
   @ 7,1 say  "gelesene A-Zeilen "+transform(nAnzA, "###,###,###,###,###")
   @ 8,1 say  "gelesene B-Zeilen "+transform(nAnzB, "###,###,###,###,###")
   @ 9,1 say  "gelesene U-Zeilen "+transform(nAnzU, "###,###,###,###,###")
   @10,1 say  "Dauer:            "+t   @11,1 say  "                                                "+str(nZeile/nDauer/104)+" Datens„tze / Sekunde"

   @13,1 say  "ENDE, bitte Taste drcken ..."

   wait

return

#endif

* Klasse zum sequentiellen Einlesen großer Dateien
* Da die Blockgröße auf 4 KB begrenzt ist, kann man auch einfach die Zeilen ausschneiden.

#include "Fileio.ch"

function TextReader( cFile )
   local oTR := TxtReader():new( cFile )
return oTR

CLASS TxtReader
  PROTECTED:
     VAR nH
     VAR nLastError
     VAR IsEOF           // Buffer hat beim Einlesen EOF erreicht, Zeilen können noch da sein !
     VAR cRest
     VAR nBufferBytes    // Anzahl der gelesenen Byte im Buffer
     VAR cCRLF, nLenCRLF // Unix/Linux nur chr(10) = 1 Byte, Windows chr(13)+chr(10) = 2 Byte
     METHOD ReadBuffer

  EXPORTED:
     METHOD Init
     METHOD Destroy
     METHOD GetLine
     METHOD GoTop
     METHOD FSize
     METHOD FError
     METHOD ErrMsg
     METHOD EOF
     METHOD IsCrLf
     METHOD IsUnix
     METHOD IsMac
ENDCLASS

METHOD TxtReader:Init( cFileName )       // Öffnet die Datei zum Lesen
   ::nLastError   := 0
   ::cRest        := ""
   ::nBufferBytes := 0
   ::nH    := fopen( cFileName , FO_READ + FO_SHARED )
   if ::nH = -1
      ::nLastError := FError()
      ::IsEOF      := .t.
   else
      ::IsEOF      := .f.
      ::ReadBuffer()
      do case
         case chr(13)+chr(10) $ ::cRest // Windows etc.
              ::cCRLF    := chr(13)+chr(10)
              ::nLenCRLF := 2
         case chr(10)         $ ::cRest // Unix
              ::cCRLF    := chr(10)
              ::nLenCRLF := 1
         case chr(13)         $ ::cRest // Mac
              ::cCRLF    := chr(13)
              ::nLenCRLF := 1
      end
   endif
RETURN SELF

METHOD TxtReader:Destroy()
   if ::nH <> -1
      FClose(::nH)
      ::nH := -1
   endif
   ::cRest := ""
RETURN SELF

METHOD TxtReader:ReadBuffer()
   local cBuffer, nBufferLen, nBytes
   if ::nH > -1
      nBufferLen := 4096
      cBuffer    := space(nBufferLen)
      nBytes     := FRead( ::nH, @cBuffer, nBufferLen)
      cBuffer    := StrTran(cBuffer,chr(26)," ")
      ::nBufferBytes += nBytes
      if nBufferLen = nBytes // mitten in Datei
         ::cRest += cBuffer
      else
         ::cRest += left(cBuffer,nBytes)
         ::IsEOF := .t.
         if FError() <> 0
            ::nLastError := FError()
         endif
      endif
      cBuffer := ""
   endif
Return

METHOD TxtReader:GetLine()
   local nPosCRLF
   local cLine := ""
   do while ! ::cCRLF $ ::cRest .and. ! ::IsEof  // Buffer einlesen, bis wir neue Zeilen haben oder die Datei gelesen wurde
      ::ReadBuffer()
   enddo
   nPosCRLF := at( ::cCRLF, ::cRest)
   if nPosCRLF > 0 // es gibt noch eine komplette Zeile, zurückgeben und kürzen
      cLine   := left(::cRest,nPosCRLF-1)
      ::cRest := substr(::cRest,nPosCRLF+::nLenCRLF)
   else
      cLine   := ::cRest
      ::cRest := ""
   endif
return cLine

METHOD TxtReader:GoTop()
   if ::nH <> -1
      FSeek(::nH, 0 , FS_SET )
   endif
   ::cRest        := ""  // zwingt zum neu einlesen
   ::ReadBuffer()
return NIL

METHOD TxtReader:FSize()
   local nSize := 0
   if ::nH <> -1
      nSize := FSize(::nH)
   endif
RETURN nSize

METHOD TxtReader:FError()
RETURN ::nLastError

METHOD TxtReader:EOF()
RETURN ::IsEOF .and. empty(::cRest)

METHOD TxtReader:IsCrLf()
RETURN (::cCRLF == chr(13)+chr(10))

METHOD TxtReader:IsUnix()
RETURN (::cCRLF == chr(10))

METHOD TxtReader:IsMac()
RETURN (::cCRLF == chr(13))

METHOD TxtReader:ErrMsg()
RETURN DosErrorMessage(::nLastError)
Gruß
Hubert
Antworten