Textzeilen einlesen, die Kommas enthalten ...

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

Moderator: Moderatoren

Antworten
Karl Fischer
Rookie
Rookie
Beiträge: 15
Registriert: Fr, 07. Mär 2008 11:44

Textzeilen einlesen, die Kommas enthalten ...

Beitrag von Karl Fischer »

Hallo Forum,

folgende einfach klingende Aufgabenstellung taucht bei mir immer
wieder auf:

Eine Textdatei soll zeilenweise in eine DBF mit einem einzigen
Feld eingelesen werden. Der Haken dabei: die Textzeilen können
beliebige Zeichen enthalten, auch Kommas, und deshalb
funtioniert das mit ...

APPEND FROM ... DELIMITED

... nicht (Text nach 1. Komma in der Zeile wird abgeschnitten).
Also habe ich versucht, die DELDBE entsprechend zu konfigurieren
durch den Funktionsaufruf ...

DBEINFO (COMPONENT_DATA, DELDBE_MODE, DELDBE_SINGLEFIELD)

... was jedoch nicht erfolgreich war: gleiches Verhalten wie vorher !
Ich denke mal, da fehlt noch was. Kann mir jemand mit einem kurzen
aber kompletten Codefragment weiterhelfen ?

Gruß
Karl
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14651
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Textzeilen einlesen, die Kommas enthalten ...

Beitrag von Jan »

Hallo Karl,

ich denke Du solltest Dich nicht an dem Komma orientieren, sondern an der Zeilenschaltung. Das scheint mir in Deinem Fall das einzig verläßliche Kriterium zu sein.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15694
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Textzeilen einlesen, die Kommas enthalten ...

Beitrag von brandelh »

Alles in ein Feld, da hätte ich an APPEND FROM ... SDF gedacht.
Wenn allerdings im Text eines Datensatzes auch noch Zeilenschaltungen erlaubt sind, dann hast du ein Problem ;-)
Gruß
Hubert
Dieter
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 237
Registriert: Do, 14. Aug 2008 14:59
Wohnort: Straelen
Hat sich bedankt: 2 Mal
Danksagung erhalten: 3 Mal

Re: Textzeilen einlesen, die Kommas enthalten ...

Beitrag von Dieter »

Hallo Karl,

gibt es einen Grund, die Textdatei nicht direkt in ein Speicherarray der Form cArray[nZeilen] zu legen? Hierfür gibt es, glaube ich, in der Wissensbasis von Hubert fertigen Code. Das Einlesen auch umfangreicher Texte geschieht unglaublich schnell. Der Arrayzugriff ist auch viel schneller.
Viele Grüße

Dieter

Was man nicht versteht, besitzt man nicht.
Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 12906
Registriert: Do, 16. Mär 2006 7:55
Wohnort: Hamburg
Hat sich bedankt: 19 Mal
Danksagung erhalten: 45 Mal

Re: Textzeilen einlesen, die Kommas enthalten ...

Beitrag von AUGE_OHR »

Karl Fischer hat geschrieben:Eine Textdatei soll zeilenweise in eine DBF mit einem einzigen
Feld eingelesen werden. Der Haken dabei: die Textzeilen können
beliebige Zeichen enthalten, auch Kommas, und deshalb funktioniert das mit APPEND FROM ... DELIMITED ... nicht ...
tja da hast du IMHO ein Problem. Du MUSSST dem Absender sagen das du es mit ";" ( Semikolon ) oder einem anderen "Trenner" haben willst den "so" wird es NICHT funktionieren.
Karl Fischer hat geschrieben:Kann mir jemand mit einem kurzen aber kompletten Codefragment weiterhelfen ?
auch wenn du einen solchen "Satz" isolieren könntest, wie willst du das Problem lösen wenn du nicht weisst "ob" ein "Komma" nun zu einem FELD gehört oder ob es ein "Trenner" sein soll ?

angenommen du hättest Vorname,Name,Strasse

Code: Alles auswählen

Meier, geb. Müller, Eva, meinestrasse 1, 21111, ...
wie willst du da dem Programm "sagen" was er welchem FELD zuordnen soll ?
gruss by OHR
Jimmy
Benutzeravatar
Rolf Ramacher
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 1930
Registriert: Do, 09. Nov 2006 10:33
Wohnort: Bergheim
Danksagung erhalten: 3 Mal
Kontaktdaten:

Re: Textzeilen einlesen, die Kommas enthalten ...

Beitrag von Rolf Ramacher »

Hallo Karl,

wenn du die sonderzeichen entfernenen mußt, schau dir aus den Tools., mal die Funktion CharRem() an
Gruß Rolf

Mitglied der Gruppe XUG-Cologne
www.xug-cologne.de
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15694
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Textzeilen einlesen, die Kommas enthalten ...

Beitrag von brandelh »

AUGE_OHR hat geschrieben:
Karl Fischer hat geschrieben:Eine Textdatei soll zeilenweise in eine DBF mit einem einzigen
Feld eingelesen werden. Der Haken dabei: die Textzeilen können
beliebige Zeichen enthalten, auch Kommas, und deshalb funktioniert das mit APPEND FROM ... DELIMITED ... nicht ...
tja da hast du IMHO ein Problem. Du MUSSST dem Absender sagen das du es mit ";" ( Semikolon ) oder einem anderen "Trenner" haben willst den "so" wird es NICHT funktionieren.
NEIN !!!!

Laut der Beschreibung ist die Aufgabe "EINE ZEILE in EIN FELD !", APPEND FROM ... DELIMITED ist schlicht der falsche Befehl, wenn man KEINE Datenfelder je Zeile haben will.
APPEND FROM ... SDF wird funktionieren, wenn das erste Feld groß genug ist.

ODER gleich in ein Array laden mit meiner Funktion ;-)
Gruß
Hubert
Karl Fischer
Rookie
Rookie
Beiträge: 15
Registriert: Fr, 07. Mär 2008 11:44

Re: Textzeilen einlesen, die Kommas enthalten ...

Beitrag von Karl Fischer »

Hallo Forum,

ich glaube, ich muss etwas weiter ausholen:

Die Textdatei wird vom Zoll gestellt und enthält pro Zeile den
TARIC-Code gefolgt von einem Semikolon gefolgt von einem
Rattenschwanz einer z.T. mehrere hundert (!) Zeichen langen
Warenbeschreibung, die außer CR/LF so ziemlich alles an Zeichen
enthalten kann inclusive , ; " ' etc. Das Format dieser Datei ist
also definitiv festgelegt - Übrigens: sie ist ein gutes Beispiel für
die pathologische Bürokratiewut in der EU, echt lesenswert !

Ich lese diese Datei nun zunächst in eine 1-Feld DBF ein und
konvertiere sie dann in eine 2-Feld DBF (1. Feld = TARIC, 2. Feld
= Rest der Zeile also Warenbeschreibung). Das Problem ist nun,
dass mit APPEND FROM ... DELIMITED alles nach dem 1. Komma
fehlt und mit APPEND FROM ... SDF ein Laufzeitfehler entsteht.
Ich muss noch prüfen, ob dieser Fehler duch zu lange Textzeile
entsteht oder dadurch, dass im SDF alle Datensätze und Felder
eine feste Länge haben müssen. Wie dem auch sei, APPEND FROM
... DELIMITED wäre die formell korrekte Lösung für diese Aufgabe
(auch laut Xbase-Dokumntation !), aber dazu muss die DELDBE
umkonfiguriert werden. Hier ist mein kleines Testprogramm, das
die DELDBE umkonfigurieren und eine Textdatei TEST.TXT in
TEST.DBF einlesen soll:

PROCEDURE MAIN
#INCLUDE "DELDBE.CH"
#INCLUDE "DBFDBE.CH"
#INCLUDE "NTXDBE.CH"
DBELOAD("DELDBE",.F.)
DBE_ALT=DBESETDEFAULT("DELDBE")
DBEINFO(COMPONENT_DATA,DELDBE_MODE,DELDBE_SINGLEFIELD)
DBESETDEFAULT(DBE_ALT)
CREATE STRUC
APPEND BLANK
REPLACE FIELD_NAME WITH "FELD"
REPLACE FIELD_TYPE WITH "C"
REPLACE FIELD_LEN WITH 256
REPLACE FIELD_DEC WITH 0
CLOSE
CREATE TEST FROM STRUC
ERASE STRUC.DBF
APPEND FROM TEST.TXT VIA DELDBE
CLOSE DATABASES
QUIT
RETURN

Das erste Proble das nun auftritt ist ein Lauzeitfehler beim Laden
der DELDBE, den ich mir absolut nicht erklären kann: "Ungültiger
Dateiname für DBE-DLL". Die DELDBE.DLL steht im aktuellen Pfad,
kann mich jemand über die im folgenden Fehlerprotokoll ausge-
wiesenen Fehlercodes aufklären ?

------------------------------------------------------------------------------
FEHLERPROTOKOLL von "C:\Pearl\test.exe" Datum: 20.03.2012 10:00:59

Xbase++ Version : Xbase++ (R) Version 1.90.331
Betriebssystem : Windows XP 05.01 Build 02600 Service Pack 3
------------------------------------------------------------------------------
oError:args :
-> VALTYPE: C VALUE: DELDBE
-> VALTYPE: L VALUE: .F.
oError:canDefault : J
oError:canRetry : N
oError:canSubstitute: N
oError:cargo : NIL
oError:description : Ungültiger Dateiname für DBE-DLL
oError:filename :
oError:genCode : 52
oError:operation : DbeLoad
oError:osCode : 0
oError:severity : 2
oError:subCode : 8009
oError:subSystem : BASE
oError:thread : 1
oError:tries : 0
------------------------------------------------------------------------------
CALLSTACK:
------------------------------------------------------------------------------
Aufgerufen von MAIN(5)

Was läuft hier schief ? Sobald das Laden der DELDBE erst mal
funktioniert, sollte das Einlesen eigentlich auch korrekt
ablaufen ...

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

Re: Textzeilen einlesen, die Kommas enthalten ...

Beitrag von brandelh »

HI,

wie groß ist die Datei ?

wenn es 200 MB nicht übersteigt, dann solltest du meine Funktion nutzen die eine Textdatei in ein einzeiliges Array einliest. Das kannst du dann aufteilen.

Code: Alles auswählen

*-------------------------------------------------------------------------------
#define CRLF chr(13)+chr(10) // bei LINUX nur chr(13), es gibt auch chr(10) als Trennzeichen.
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
   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
         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 > len(cDateiInhalt)
               exit
            endif
         enddo
      endif
   endif
return aZeilen
Du könntest die Funktion abwandeln oder bei größeren Dateien die IO Funktionen nutzen, die Alaska mal freigegeben hat, hier meine Ausführung:

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 )
Gruß
Hubert
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: Textzeilen einlesen, die Kommas enthalten ...

Beitrag von Koverhage »

Warum der Umweg über das Array ?
Mit der Funktion von Hubert, über memoread die Datei einlesen (wenn zu groß mit fopen, freadstr, etc)
dann Zeile für Zeile in dbf schreiben
1. Feld mit AT auf ;
2. Feld = Rest der Zeile
Gruß
Klaus
Karl Fischer
Rookie
Rookie
Beiträge: 15
Registriert: Fr, 07. Mär 2008 11:44

Re: Textzeilen einlesen, die Kommas enthalten ...

Beitrag von Karl Fischer »

Hallo Forum,

ich habe das Problem gelöst, eine Codezeile genügt:

DBIMPORT("TEST.TXT",,,,,,,"DELDBE",{{DELDBE_MODE,DELDBE_SINGLEFIELD}})

Damit wird die Textdatei TEST.TXT Zeile für Zeile via DELDBE in die gerade
offene 1-Feld DBF eingelesen, wobei innerhalb der Zeile beliebige Zeichen
stehen dürfen. Mit FREADSTR() geht das übrigens nicht, denn das Feld-
trennzeichen ist dort CHR(0) anstatt CR/LF. Trotzdem vielen Dank für Eure
Antworten !

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

Re: Textzeilen einlesen, die Kommas enthalten ...

Beitrag von brandelh »

Karl Fischer hat geschrieben:Mit FREADSTR() geht das übrigens nicht, denn das Feldtrennzeichen ist dort CHR(0) anstatt CR/LF.
Trotzdem vielen Dank für Eure Antworten !
Gruß
Karl
:!: falsch :!:
BrandelH hat geschrieben:FREADSTR() wird von einem chr(0) unterbrochen, das stimmt, aber es wird auch durch chr(13)+chr(10) unterbrochen !
In der Hilfe steht also nur die Ausnahme, die aber sehr wichtig ist, wenn man Textdateien mit möglichen chr(0) lesen muss.
Bei mir waren es gepackte numerische Felder, die nicht richtig aufgelöst wurden ... zum Glück habe ich damals rechtzeitig bemerkt, dass die Hälfte der Daten fehlte :wink:
:!: richtig :!:
Wie es in der Hilfe steht, liefert FReadStr() alle angeforderten Zeichen und wird nur von einem chr(0) unterbrochen.
Gruß
Hubert
Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 12906
Registriert: Do, 16. Mär 2006 7:55
Wohnort: Hamburg
Hat sich bedankt: 19 Mal
Danksagung erhalten: 45 Mal

Re: Textzeilen einlesen, die Kommas enthalten ...

Beitrag von AUGE_OHR »

brandelh hat geschrieben:Laut der Beschreibung ist die Aufgabe "EINE ZEILE in EIN FELD !"
uuuups ...
Karl Fischer hat geschrieben:DBIMPORT("TEST.TXT",,,,,,,"DELDBE",{{DELDBE_MODE,DELDBE_SINGLEFIELD}})
JA dann ist DELDBE_SINGLEFIELD das richtige.
Karl Fischer hat geschrieben:Die Textdatei wird vom Zoll gestellt und enthält pro Zeile den TARIC-Code
machst du die Zoll Sachen "offline" oder benutzt du "ATLAS" ? hast du dazu Schnittstellen Beschreibungen ( und woher bekommen ) ?
gruss by OHR
Jimmy
Karl Fischer
Rookie
Rookie
Beiträge: 15
Registriert: Fr, 07. Mär 2008 11:44

Re: Textzeilen einlesen, die Kommas enthalten ...

Beitrag von Karl Fischer »

... machst du die Zoll Sachen "offline" oder benutzt du "ATLAS" ?
Nein, nicht online mit ATLAS, das sind einfache Textdateien
die der Zoll publiziert.

Gruß
Karl
Antworten