ich muss regelmäßig große Textdateien einlesen. Bisher habe ich ein bei Clipper enthaltene Funktionssammlung für zeilenweises Einlesen genutzt (CL5 Beispiel: FILEIO.PRG).
Nun brauchte ich es schneller ... die hier gezeigte Klasse liest die Daten 3 x so schnell ein wie die alte Funktion ( 7 Millionen Zeilen in 40 Sekunden ...)
Die Zeit berücksichtigt nur das Einlesen von Zeilen in den Speicher, nicht das Schreiben in eine neue DBF etc.
Die Klasse sollte auch mit UNIX (nur LF) als Zeilentrenner zurecht kommen und versehentliche chr(26) nach Blank tauschen.
Bitte testet die Klasse, damit sie robuster wird, danach stelle ich sie in die Wissensbasis ...
Code: Alles auswählen
#define TESTDATEI ...
procedure main
local nZeile := 0
local cZeile := ""
local nSize, nBytes := 0
local nDauer
local oTR
local nAnzLieferBG, nAnzRM
cls
oTR := TextReader():new( TESTDATEI )
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,"###,###,###,###,###")
nAnzLieferBG := 0
nAnzRM := 0
nDauer := seconds()
do while ! oTR:EOF()
cZeile := oTR:GetLine()
* if len(cZeile)=0
* exit
* endif
do case
case cZeile = "LIEFER_BG"
nAnzLieferBG++
case cZeile = "RM_MED"
nAnzRM++
end case
nZeile++
nBytes += len(cZeile)+2 // CR+LF = 2 Byte
if nZeile % 1000 = 0
@ 4,1 say "gelesene Bytes "+transform(nBytes,"###,###,###,###,###")
@ 5,1 say "gelesene Zeilen "+transform(nZeile,"###,###,###,###,###")
endif
enddo
oTR:Destroy()
nDauer := seconds() - nDauer
@ 4,1 say "gelesene Bytes "+transform(nBytes,"###,###,###,###,###")
@ 5,1 say "gelesene Zeilen "+transform(nZeile,"###,###,###,###,###")
@ 7,1 say "Dauer: "+transform(nDauer,"###,###,###,###,###")+" Sekunden => "+str(nZeile/nDauer)+" Zeilen / Sekunde"
@ 8,1 say " "+str(nZeile/nDauer/104)+" Datensätze / Sekunde"
@10,1 say "nAnzLieferBG: "+transform(nAnzLieferBG,"###,###,###,###,###")
@11,1 say "nAnzRM: "+transform(nAnzRM,"###,###,###,###,###")
@13,1 say "ENDE, bitte Taste drücken ..."
wait
return
* 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"
CLASS TextReader
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 TextReader: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 TextReader:Destroy()
if ::nH <> -1
FClose(::nH)
::nH := -1
endif
::cRest := ""
RETURN SELF
METHOD TextReader: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 TextReader: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 TextReader:GoTop()
if ::nH <> -1
FSeek(::nH, 0 , FS_SET )
endif
::cRest := "" // zwingt zum neu einlesen
::ReadBuffer()
return NIL
METHOD TextReader:FSize()
local nSize := 0
if ::nH <> -1
nSize := FSize(::nH)
endif
RETURN nSize
METHOD TextReader:FError()
RETURN ::nLastError
METHOD TextReader:EOF()
RETURN ::IsEOF .and. empty(::cRest)
METHOD TextReader:IsCrLf()
RETURN (::cCRLF == chr(13)+chr(10))
METHOD TextReader:IsUnix()
RETURN (::cCRLF == chr(10))
METHOD TextReader:IsMac()
RETURN (::cCRLF == chr(13))
METHOD TextReader:ErrMsg()
RETURN DosErrorMessage(::nLastError)