Textdatei in DBF übertragen

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

Moderator: Moderatoren

Antworten
JanR
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 129
Registriert: Di, 18. Okt 2005 14:44

Textdatei in DBF übertragen

Beitrag von JanR »

Hallo,

wieder ein Problem.

Ich habe eine Textdatei in der die Spalten mit TAB getrennt sind. Es gib in manchen Zeilen 2 Spalten, dann mal 4 Spalten, usw.

Wie kann ich die Daten am besten in eine DBF übertragen?

Habe jetzt eine Datenbank mit 4 Spalten angelegt. Das Problem ist aber, wenn ich z. B. aus der Textdatei die 4. Spalte einer Zeile in die DBF übertragen möchte, obwohl in der Textdatei nur 3 Spalten in der Zeile sind, dass mir das Programm crasht.

Irgendeine Lösung?
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14641
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 87 Mal
Kontaktdaten:

Beitrag von Jan »

Wie sieht denn der Code aus?

Kannst Du nicht einbauen, daß im Fall eines fehlenden Wertes ein Standardwert gespeichert wird?

Jan
JanR
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 129
Registriert: Di, 18. Okt 2005 14:44

Beitrag von JanR »

Wie meinst du das? In der Textdatei kann ich nichts ändern, die bekomme ich. Und wenn ich z.b. die 4te Spalte auslesen will, obwohl die in der Textdatei nicht vorhanden ist, macht es peng.
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14641
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 87 Mal
Kontaktdaten:

Beitrag von Jan »

Ist schon klar.

Aber es gibt ja eine Struktur: Wert1 TAB Wert2 TAB Wert3 TAB Wert4 CRLF

Die solle für jede Zeile gleich sein. Oder habe ich da etwas falsch verstanden?

Wenn also irgendwo steht TAB TAB dann weiß Du, daß da eigentlich ein Wert zwischengehört. Genauso, wenn da ein TAB CRLF auftaucht.

Und da schreibst Du dann eben einen von Dir zu bestimmenden Wert in Deine dbf. Ob das jetzt 0 oder NULL oder "" ist, das mußt Du wissen.

Jan
JanR
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 129
Registriert: Di, 18. Okt 2005 14:44

Beitrag von JanR »

Hier ein Beispiel:

Zeile1: Wert1 TAB Wert2
Zeile2: Wert1 TAB Wert2 TAB Wert3 TAB Wert4
Zeile3: Wert1 TAB Wert2 TAB Wert3 TAB TAB

Die Struktur ist nicht einheitlich, sondern in fast jeder Zeile unterschiedlich.
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14641
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 87 Mal
Kontaktdaten:

Beitrag von Jan »

OK. Verstanden. Aber das Prinzip ist gleich. Du musste einen Zähler einbauen, der vor jedem Zeilenvorschub (also vermutlich CRLF) die Werte zählt. Und sind das weniger als 4 müssen die ergänzt werden.

Also z.B. eine Schleife für jede Zeile, die bei jedem Wert 1 hochzählt.

Aber ohne Deinen Code zu kennen ist es schwierig zu sagen wie das genau auszusehen hat.

Jan
JanR
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 129
Registriert: Di, 18. Okt 2005 14:44

Beitrag von JanR »

Hi Jan,

da verstehe ich leider gerade nur Bahnhof.

Ich bekomme eine Textdatei. Im Aufbau her wie ich im letzten Beitrag geschrieben habe.

Diese öffne ich mit der DELDBE.

Jetzt erstelle ich eine neue Datenbank und möchte die Daten übertragen. Hier der Quellcode:

Code: Alles auswählen

   aStruct := {;
                 {"FIELD1", "N",   1, 0},;
                 {"FIELD2", "C", 100, 0},;
                 {"FIELD3", "C",   8, 2},;
                 {"FIELD4", "C",   8, 2} ;
              }

   DbCreate("DATENBANK.DBF", aStruct, "DBFNTX")

   USE DATENBANK.dbf ALIAS DATENBANK VIA DBFNTX NEW EXCLUSIVE

   FOR i := 1 TO TEXTDATEI->(RecCount())
      DATENBANK->(DbAppend())
  
      REPLACE FIELD1 WITH TEXTDATEI->FIELD1,;
              FIELD2 WITH TEXTDATEI->FIELD2,;
              FIELD2 WITH TEXTDATEI->FIELD3,;
              FIELD4 WITH TEXTDATEI->FIELD4

      TEXTDATEI->(DbSkip())
   NEXT
[/code]
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14641
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 87 Mal
Kontaktdaten:

Beitrag von Jan »

OK, jetzt verstehe ich besser. ich dachte, Du hättest die Textdatei irgendwie in einen String eingelesen.

Ich muß gestehen, daß ich mich mit Datenbanktreibern für Textdateien nicht auskenne. ich weiß also nicht, wie da die Werte belegt sind wenn in der Textdatei der Wert nicht existiert.

Versuch doch mal, ob das so geht:

Code: Alles auswählen

REPLACE FIELD1 WITH IIF(TEXTDATEI->FIELD1 = NULL, 0, TEXTDATEI->FIELD1,;
        FIELD2 WITH IIF(TEXTDATEI->FIELD2 = NULL, "", TEXTDATEI->FIELD2,;
        FIELD3 WITH IIF(TEXTDATEI->FIELD3 = NULL, "", TEXTDATEI->FIELD3,;
        FIELD4 WITH IIF(TEXTDATEI->FIELD4 = NULL, "", TEXTDATEI->FIELD4
Vielleicht hilft das ja.

Jan
Benutzeravatar
Manfred
Foren-Administrator
Foren-Administrator
Beiträge: 21165
Registriert: Di, 29. Nov 2005 16:58
Wohnort: Kreis Wesel
Hat sich bedankt: 206 Mal
Danksagung erhalten: 67 Mal

Beitrag von Manfred »

Hallo JanR

mache es doch ganz einfach... Ich würde mir im Debugger anschauen, was denn passiert, wenn Du auf eine Field zugreifst, was nichts hat. Das ist m.E. die einfachste Methode um zu sehen, wodrauf Du dann prüfen mußt.
Gruß Manfred
Mitglied der XUG Osnabrück
Schatzmeister des Deutschsprachige Xbase-Entwickler e.V.
großer Fan des Xbaseentwicklerwiki https://wiki.xbaseentwickler.de/index.p ... Hauptseite
Doof kann man sein, man muß sich nur zu helfen wissen!!
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:

Beitrag von brandelh »

Hallo Jan,

wenn man TXT Daten einlesen muß, sollte man die DELDBE nicht nutzen !
Diese setzt meines Wissens genau gleichen Satzaufbau und Satzlänge voraus.
Wenn alles Text ist, kann man append mit SDF nutzen, aber die TAB sind da eher ungeeignet.
Xbase geht - im Gegensatz zu Clipper - immer von tatsächlich gleichlangen Sätzen aus (angeblich eine Definition von SDF ...).

Ich mache es immer so:

cTxt := memoread(cDatei) // kein Problem auch bei 300 MB !

MEMOLINE() ist bie kleinen Dateien schön, aber bei tausenden von Zeilen muss man sich selbst kümmern. Immer nächsten suchen mit at() CRLF und die Einzelteile rausholen. Achtung nicht den String verändern (Sätze ausschneiden und kürzen), da das bei großen sehr langsam ist.
einfach den Anfang und das Ende merken und darin dann nach den Feldern suchen.

Ich habe jetzt kein Beispiel, aber es gab mal sowas über schnelle Stringbearbeitung etc...
Gruß
Hubert
JanR
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 129
Registriert: Di, 18. Okt 2005 14:44

Beitrag von JanR »

Hallo Hubert,

das ist eine gute Idee. Aber wie zerteile ich die Zeilen in Spalten bzw. wie finde ich den Tab zwischen den Spalten? Mit At() erhalte ich immer 0 zurück.
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14641
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 87 Mal
Kontaktdaten:

Beitrag von Jan »

Der Vorschlag von Hubert war das, wo ich von ausgegangen war, zu Beginn der Diskussion. Da kann man dann nämlich die Tabs bis zum CRLF mitzählen und dann korrigierend eingreifen.

Ich habe das Zerteilen mal in einem älteren Programm eingebaut - habe ich aber zu Hause auf dem Rechner liegen. Ich schau nachher mal rein und würde das dann hier posten.

Jan
Benutzeravatar
Wolfgang Ciriack
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2932
Registriert: Sa, 24. Sep 2005 9:37
Wohnort: Berlin
Hat sich bedankt: 13 Mal
Danksagung erhalten: 34 Mal
Kontaktdaten:

Beitrag von Wolfgang Ciriack »

Hallo Jan,
ich benutze zum Zerlegen von Textdateien immer die XbTools-Funktionen (weiss natürlich nicht ob du die Tools hast).

Damit geht das relativ einfach (Quick and dirty Beispiel):

Code: Alles auswählen

trzch:=";"
offset:=0
laenge:=filesize(impdatei)
do while offset+10<laenge
      * Zeilen nicht länger als 600 Zch.
      tmp:=filestr(impdatei,600,offset)
      * Trennzeichen ist CRLF
      satztrenn:=at(chr(10),tmp)
      offset+=satztrenn
      satz:=substr(tmp,1,satztrenn-1)
      ** satz ist mein Datensatz
      i:=0
      ** benötige nur bestimmte
      tokeninit(@satz,trzch,1)
      do while !tokenend()
          vari:=tokennext(satz)
          if !empty(vari)
              do case
              case i=0
                  v1:=vari
              case i=2
                  v2:=val(vari)
              .....
              ......
              endcase
          endif
          i++
      enddo
      if v2<>0
         append ....
         replace ....
      endif
enddo

Viele Grüße
Wolfgang
JanR
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 129
Registriert: Di, 18. Okt 2005 14:44

Beitrag von JanR »

Hi,

die XbTools habe ich leider nicht. Gibt es da keine andere Lösung? Bin langsam am verzweifeln :(
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14641
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 87 Mal
Kontaktdaten:

Beitrag von Jan »

Jan,

nicht verzagen, Forum fragen.... Da wird Sie geholfen. Wir kriegen das schon mit alle Mann (wieso eigentlich keine Frauen?) zusammen irgendwie hin.

Trotzdem eine schlechte Nachricht: Ich habe da alte Projekt anscheinend in einem Anfall von Wahnvorstellungen gelöscht. Mist! Aber gib mir ein wenig Zeit, ich versuche mal, ob ich das noch rekonstruieren kann. Und ich bin mir sehr sicher, daß ich das ohne Tools gemacht habe.

Apropos Tools: Hast Du keine Subscription? Da wären die dann automatisch mit bei. Muß ja nicht gleich die ultrateure sein, die kleine reicht ja auch schon...

Jan
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

Beitrag von AUGE_OHR »

hi,
JanR hat geschrieben: die XbTools habe ich leider nicht. Gibt es da keine andere Lösung? Bin langsam am verzweifeln :(
ich mache es zwar immer über FOPEN, aber für MEMOREAD gibts
auch eine Lösung :

Code: Alles auswählen

LOCAL cText     := MemoRead( cSysFilename ) 
LOCAL nMaxLines := MlCount( cText, 80 ) 
LOCAL cLine, n 

     FOR n:=1 TO nMaxLines 

         cLine := Trim( MemoLine( cText, 80, n ) ) 
         DoMyTAB(cLine)
         
     NEXT 

in DoMyTAB kannst du nun die Zeile "zerpflücken"

gruss by OHR
Jimmy
p.s. sind in dem Textfile "Umlaute" ?
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14641
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 87 Mal
Kontaktdaten:

Beitrag von Jan »

Mensch Jimmy, musste das sein? Ich war mitten drin im Testen, und jetzt kommst Du mir zuvor! Immer diese Profi-Programmierer... immer diese beinahe-Hamburger...

Aber das war genau der Weg, den ich damals auch gegangen bin. Und es klappt ganz wunderbar.

Jan
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:

Beitrag von brandelh »

Hallo Jan,

du solltest Smilies setzen wie
:wink: oder ;-) wenn du etwas scherzhaft sagen willst.
Man könnte das sonst falsch verstehen wenn man sich nicht gut kennt. :?
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:

Beitrag von brandelh »

Hallo JanR,

es kommt ganz auf die Anzahl der Zeilen der Importdatei an und auf die Zeit, die es dauern darf. Die Lösung von JIMMY ist die Standard Clipper-Lösung. Das Problem ist die Verarbeitungszeit, die bei ein paar tausend Zeilen noch gut ist, danach aber explodiert. Memoline merkt sich die aktuelle Zeile nicht !

Wenn du also was schnelleres für große Dateien brauchst, dann muß man selbst die Satzgrenzen ermitteln (so aus dem Gedächtnis, also eventuell Tippfehler enthalten ;-) ) :

Code: Alles auswählen

#define CRLF chr(13)+chr(10)
cTXT        := MemoRead(...)  // diesen TEXT nicht mehr ändern !
nZeile      := 1
nZeiVon   := 1 
nZeiBis    := 0
nTab        := 0

do while .t. // aktuelle nZeiVon ist schon bekannt
    // geht erst ab 1.82:
    // At( <cSubString>, <cString>, [<nStartPos>] ) --> nPosition  
    nZeiBis  := At( CRLF, cTXT, nZeiVon ) // 1. Zeichen von CRLF !
    if nZeiBis > 0
       cZeile := substr(cTXT,nZeiVon,nZeiBis-nZeiVon)
    else
       cZeile := substr(cTXT,nZeiVon)  // nun den Rest der Datei
       nZeiBis := len(cZeile)               //Abbruchbedingung
    endif
    // mit cZeile kann man spielen, da kurz...
    do while .t.
         nTab := at(chr(9),cZeile)
         if nTab > 0
            uFeld := left(cZeile,nTab-1)
            cZeile := substr(cZeile,nTab+1)
         else   // alle Felder ausgelesen
            uFeld := cZeile
            cZeile := ""
         endif
         ... tu was damit
         if empty(cZeile)
            exit
         endif
    enddo 
    // Datensatz ist verarbeitet
    nZeiVon := nZeiBis+2  // CR + LF = 2 Zeichen
    if nZeiVon > len(cTXT)
       exit
    endif
enddo
Diese Methode ist sehr schnell, da der Hauptstring im Speicher nicht mehr bewegt wird. Ich nutze diese Art bei Dateien mit 300 MB und vielen Millionen von Zeilen, es ist wirklich flexibel und flott. Der Arbeitsspeicher des Rechners sollte allerdings das doppelte an Arbeitsspeicher haben wie die Datei groß ist (250 MB Datei -> 512 MB Rechner, ich habe 2 GB ;-) )

Der AT Befehl hat erst ab 1.82 den 3. Parameter ...

Ich habe da noch eine Funktion, die die Datei auf Festplatte Zeilenweise einließt, ich meine die ist auch auf den Alaska Seiten ...
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:

Beitrag von brandelh »

Hi,

hier ist die Datei mit der man Textdateien direkt zeilenweise auslesen kann (das Original stammt noch von Clipper wie ich gerade im Header lesen kann):

Code: Alles auswählen

* Funktionen zur Behandlung von ASCII Textdateien
* Anpassung von Original CL5 Beispielen: FILEIO.PRG
*
* Im Gegensatz zu Original Funktion, wird bei einem String
* nie CHR(26) zurückgegeben -> Markiert ein Dateiende
* und CR+LF wird am Ende des Strings abgeschnitten.
*

#ifdef TEST
   cls
   nDateiHandle := TxtDatOpen( "manuRech.lst" )
   ? 'TxtDatOpen( "AutoRech.lst" )', nDateiHandle
   ?
   ?
   nZeile := 0
   cZeile := ""

   do while .t.

      cZeile := TxtDatRead( nDateiHandle,1,100 )

      if len(cZeile)=0
         exit
      endif

      ?? cZeile

      if chr(26) $ cZeile
         ?? "CHR(26)"
      endif
      if chr(13) $ cZeile
         ?? "CR"
      endif
      if chr(10) $ cZeile
         ?? "LF"
      endif

      nZeile++
   enddo

   ? "ENDE"

#endif


#include "FileIO.ch"
#include "common.ch"
*
* Funktionen   zur Bearbeitung von ASCII Textdateien
*
*--------------------------------------------------------------
function TxtDatOpen( cDatName, nModus )
return fopen( cDatName, nModus )
*--------------------------------------------------------------
function TxtDatCreate( cDatName, nModus )
return fcreate( cDatName, nModus )
*--------------------------------------------------------------
function TxtDatClose( nHandle )
return fclose( nHandle )
*--------------------------------------------------------------
function TxtDatGoTop( nHandle )           // gibt aktuelle Position zurück
   RETURN ( FSEEK( nHandle, 0 ) )
*--------------------------------------------------------------
function TxtDatGoBottom( nHandle )        // gibt aktuelle Position zurück
   RETURN ( FSEEK( nHandle, 0, FS_END ) )
*--------------------------------------------------------------
function TxtDatPos( nHandle )
   RETURN ( FSEEK( nHandle, 0, FS_RELATIVE ) )
*--------------------------------------------------------------
function TxtDatWrite( nHandle, cTxt, nAnz )
   nAnz := fWrite(nHandle, cTxt, nAnz)
   RETURN nAnz
*--------------------------------------------------------------
function TxtDatSize( nHandle )
   LOCAL nCurrent
   LOCAL nLength

   nCurrent := TxtDatPos( nHandle )   // Aktuelle Position sichern
   nLength := TxtDatGoBottom( nHandle )   // Länge ermitteln
   FSEEK( nHandle, nCurrent )             // Dateizeiger zurücksetzen

   RETURN nLength
*--------------------------------------------------------------
function TxtDatEOF( nHandle )
   LOCAL nCurrent, lEOF

   nCurrent := TxtDatPos( nHandle )   // Aktuelle Position sichern

   lEOF := (nCurrent = TxtDatGoBottom( nHandle ))
                                          // EOF = .t. wenn
                                          // Aktuelle Position gleich
                                          // Dateiende ist !

   FSEEK( nHandle, nCurrent )             // Dateizeiger zurücksetzen

   RETURN lEOF
*--------------------------------------------------------------
/***
*
*  TxtDatRead( <nHandle>, [<nLines>], [<nLineLength>], [<cDelim>] ) --> cLines
*
*  Liest eine oder mehrere Zeilen aus einer Textdatei
*
*  HINWEIS:
*  In die Zeilenlänge gehen die Begrenzer mit ein, so daß die maximale
*  Länge einer gelesenen Zeile (nLineLength - LEN( cDelim )) ist.
*
*     Vorgaben: nLines=1, nLineLength=80, cDelim=CRLF
*
*     Fehlerprüfung muß über FERROR() erfolgen.
*
*     FReadLn() gibt eine leere Zeichenkette ("") zurück, wenn EOF
*     erreicht wird.
*
*/
function TxtDatRead( nHandle, nLines, nLineLength, cDelim )

   LOCAL nCurPos        // Aktuelle Position in Datei
   LOCAL nFileSize      // Dateigröße
   LOCAL nChrsToRead    // Anzahl der zu lesenden Zeichen
   LOCAL nChrsRead      // Anzahl der aktuell gelesenen Zeichen
   LOCAL cBuffer        // Lese-Puffer
   LOCAL cLines         // Rückgabe - Anzahl der gelesenen Zeilen
   LOCAL nCount         // Zähler der gelesenen Zeilen
   LOCAL nEOLPos        // Position von EOL (End Of Line) in cBuffer

   DEFAULT nLines      TO 1
   DEFAULT nLineLength TO 80
   DEFAULT cDelim      TO ( CHR(13) + CHR(10) )

   nCurPos   := TxtDatPos( nHandle )
   nFileSize := TxtDatSize( nHandle )

    // Sicherstellen, daß kein Lesezugriff hinter EOF erfolgt
   nChrsToRead := MIN( nLineLength, nFileSize - nCurPos )

   cLines  := ''
   nCount  := 1
   DO WHILE (( nCount <= nLines ) .AND. ( nChrsToRead != 0 ))

      cBuffer   := SPACE( nChrsToRead )
      nChrsRead := FREAD( nHandle, @cBuffer, nChrsToRead )

      // Fehlerbedingung prüfen
      IF !(nChrsRead == nChrsToRead)
         // Fehler!
         // Um konzeptionell mit den low-level Dateifunktionen
         // kompatibel zu bleiben, wird der Benutzer gezwungen
         // FERROR() auszuwerten (wurde durch FREAD() gesetzt),
         // um den Fehler zu bemerken.
         //
         nChrsToRead := 0
      ENDIF

      nEOLPos := AT( cDelim, cBuffer )


      // Puffer und aktuelle Dateiposition aktualisieren
      IF ( nEOLPos == 0 )
         cLines  += LEFT( cBuffer, nChrsRead )
         nCurPos += nChrsRead
      ELSE
         cLines  += LEFT( cBuffer, ( nEOLPos + LEN( cDelim ) ) - 1 )
         nCurPos += ( nEOLPos + LEN( cDelim ) ) - 1
         FSEEK( nHandle, nCurPos, FS_SET )
      ENDIF

      // CHR(26) = EOF Char in Textdateien beachten !

      if chr(26) $ cLines   // Achtung CHR(26) in String enthalten.
         cLines  := LEFT( cLines, at( chr(26),cLines )-1 ) // Kürzen vor Chr(26)
         TxtDatGoBottom( nHandle )    // Datei auf EOF setzen
         exit                         // Schleife verlassen
      endif

      // Sicherstellen, daß kein Lesezugriff hinter EOF erfolgt
      IF (( nFileSize - nCurPos ) < nLineLength )
         nChrsToRead := ( nFileSize - nCurPos )
      ENDIF

      nCount++

   ENDDO

   if right( cLines , 2 ) = CHR(13) + CHR(10)
      cLines := left( cLines , len(cLines)-2 )
   endif

   RETURN ( cLines )

[quote][/quote]
Gruß
Hubert
Antworten