AW: csv Datei Import ClassHelper für TClientDataSet
Zitat:
Delphi-Quellcode:
geschrieben, der das umsetzt:
TTextReader
Delphi-Quellcode:
type
TTextReaderHelper = class helper for TTextReader function CharCount(const ALine: string; AChar: Char): Integer; procedure ReadQuotedLine(Target: TStrings); end; function TTextReaderHelper.CharCount(const ALine: string; AChar: Char): Integer; var C: Char; begin result := 0; for C in ALine do begin if C = AChar then Inc(result); end; end; procedure TTextReaderHelper.ReadQuotedLine(Target: TStrings); var line: string; line2: string; saveStrictDelimiter: Boolean; begin line := ReadLine; if Odd(CharCount(line, Target.QuoteChar)) then begin { Eine ungerade Anzahl von Quotes bedeutet, daß der gequotete String mindestens einen Zeilenumbruch enthält. Wir hängen also die nachfolgenden Zeilen mit LineBreak an, bis eine weitere Zeile mit ungerader Anzahl Quotes kommt. } repeat line := line + sLineBreak; line2 := ReadLine; line := line + line2; until Odd(CharCount(line2, Target.QuoteChar)); end; { Zeilenumrüche in line dürfen nicht in separate Einträge zerlegt werden! } saveStrictDelimiter := Target.StrictDelimiter; try Target.StrictDelimiter := True; Target.CommaText := line; finally Target.StrictDelimiter := saveStrictDelimiter; end; end; |
AW: csv Datei Import ClassHelper für TClientDataSet
@Schokohase
Zitat:
Try it! @Rollo62 Header wird erzeugt wenn nicht vorhanden @Uwe Raabe Auch elegant gelöst! Ist aber nur ein Teil der Aufgabe;) |
AW: csv Datei Import ClassHelper für TClientDataSet
Zitat:
Code:
Meine Testdaten
EStringListError: Listenindex außerhalb des gültigen Bereichs (3)
Delphi-Quellcode:
oder als Text
'a;b;c;d' + sLineBreak +
'a;"b;b";"c'#10'c";d'
Code:
Du solltest schon wissen, was dir TStringList abnimmt und vor allem ab wann.
a;b;c;d
a;"b;b";"c c";d Im Übrigen fängst du erst ab der 2. Zeile mit dem Import an - auch wenn der Parameter
Delphi-Quellcode:
auf
FirstLineTitle
Delphi-Quellcode:
steht.
false
|
AW: csv Datei Import ClassHelper für TClientDataSet
Zitat:
Code:
If X=0 and (not FirstLineTitle)
Zitat:
Und wie oft ist dir dein Beispiel im Alltag begegnet? Wobei auch deine Konstellation unter meinen Voraussetzungen läuft. Hmm:roll: |
AW: csv Datei Import ClassHelper für TClientDataSet
Zitat:
Ich kann es dir also nicht sagen, wie oft mein Beispiel im Alltag vorkommt, denn keiner bemerkt es, weil es anstandslos funktioniert. |
AW: csv Datei Import ClassHelper für TClientDataSet
Zitat:
Zitat:
|
AW: csv Datei Import ClassHelper für TClientDataSet
Zitat:
Die zweite Zeile sieht in Delphi-String-Schreibweise so aus
Delphi-Quellcode:
.
'a;"b;b";"c'#13#10'c";d'
(aber auch so ist es gültig
Delphi-Quellcode:
)
'a;"b;b";"c'#10'c";d'
Sieht du jetzt was da zwischen den Quote-Zeichen steht? Das ist 100% gültig nach RFC4180. PS Wenn du wissen möchtest, was so alles RFC4180 konform ist, dann kannst du gerne deine Dateien mit dem Webdienst http://csvlint.io/ überprüfen lassen. Allerdings wird dort zwingend das Komma als Feld-Trenner gefordert. Und diese Datei
Code:
findet der total gut (was auch zu erwarten war, weil RFC4180 konform)
a,b,c,d
aa,"b,b","c c",dd |
AW: csv Datei Import ClassHelper für TClientDataSet
Du sprichst immer von Dateien, könntest du mir das Beispiel als Datei zur Verfügung stellen?
Hab keine Lust dafür zu coden und möchte auch keinen Hex-Editor dafür bemühen. |
AW: csv Datei Import ClassHelper für TClientDataSet
Zitat:
|
AW: csv Datei Import ClassHelper für TClientDataSet
Zitat:
Selbst mit Notepad+ gelingt es mir nicht, sonst würde ich nicht danach fragen. |
AW: csv Datei Import ClassHelper für TClientDataSet
Liste der Anhänge anzeigen (Anzahl: 1)
Ohne Worte
Obwohl, das ist jetzt Copy-Paste der Beispiel-Daten aus dem Beitrag #47 in Visual Studio Code eingefügt und als CSV-Datei abgespeichert. Währenddessen habe ich auch noch ein- und ausgeatmet. |
AW: csv Datei Import ClassHelper für TClientDataSet
@Schokohase
Ich bin ja froh das deine Datei auch nicht die Anforderung erfüllt Zitat:
Delphi-Quellcode:
procedure TClientDataSetCsvClassHelper.LoadFromFile;
var myString :Variant; slFile :TStringList; slRow :TStringList; x,i,j :Integer; myPos :Integer; myFields :Integer; cntFields :Integer; begin slFile := TStringList.Create; slRow := TStringList.Create; try myPos := 0; slRow.Delimiter := Delimiter; slRow.QuoteChar := QuoteChar; slRow.StrictDelimiter := True; slFile.StrictDelimiter := True; slFile.LoadFromFile( Filename ); // ClientDataset Initialisieren if slFile.Count > 0 then begin Active := False; for x := 0 to slFile.Count - 1 do begin myString := ''; if (x = 0) then begin myPos := x; // Build Header for DS slRow.DelimitedText := slFile[0]; if FirstLineTitle then myFields := slRow.Count else begin if (length( slFile[0] )- length( stringreplace( slFile[0], QuoteChar,'',[rfreplaceall, rfIgnoreCase])) mod 2 = 0) then myFields := length( slFile[0] )- length( stringreplace( slFile[0], Delimiter,'',[rfreplaceall, rfIgnoreCase])) else myFields := slRow.Count; FieldDefs.Clear; for i := 0 to myFields - 1 do begin FieldDefs.Add( Format( 'Field%d',[i]), ftWideString, StringLength ); end; end; if FirstLineTitle then begin for i := 0 to myFields - 1 do FieldDefs.Add( slRow[i], ftWideString, StringLength ); end; CreateDataSet; Active := True; myString := slFile[myPos]; end; begin if (x=0) and FirstLineTitle then inc( myPos ); if myPos = slFile.Count then break; myString := slFile[myPos]; cntFields := length( myString )- length( stringreplace( myString, Delimiter,'',[rfreplaceall, rfIgnoreCase])); while cntFields <= (myFields-1) do if FirstLineTitle and (cntFields < (myFields-1)) then begin inc( myPos ); myString := myString+slFile[myPos]; cntFields := length( myString )- length( stringreplace( myString, Delimiter,'',[rfreplaceall, rfIgnoreCase])); end else if not FirstLineTitle and (x <> 0) then begin inc( myPos ); myString := slFile[myPos]; if (length( myString )- length( stringreplace( myString, QuoteChar,'',[rfreplaceall, rfIgnoreCase])) mod 2 <> 0) then begin myString := slFile[myPos-1]+myString; cntFields := length( myString )- length( stringreplace( myString, Delimiter,'',[rfreplaceall, rfIgnoreCase])); end else cntFields := length( myString )- length( stringreplace( myString, Delimiter,'',[rfreplaceall, rfIgnoreCase])); end else break; end; // fill TClientDataset DisableControls; begin slRow.DelimitedText := myString; Append; for j := 0 to slRow.Count - 1 do begin Fields[j].AsString := slRow[j]; end; Post; end; EnableControls; inc( myPos ); end; end; finally slFile.Free; slRow.Free; end; end; |
AW: csv Datei Import ClassHelper für TClientDataSet
Ich gehe davon aus, das dieser Code nun fast RFC-konform ist (noch nicht ganz, da die Feldermittlung ohne FirstTitleLine noch Optimierungsbedarf hat).
Eine Textdatei mit CrLF und LF kommt einem im Alltag nicht unter, außer vielleicht zu Ostern:wink: |
AW: csv Datei Import ClassHelper für TClientDataSet
Erkläre doch bitte, warum meine Testdaten nicht RFC konform sein sollten.
Wenn Daten aus einem System (Datenbank) exportiert werden, dann können sich wenigstens meine Kunden darauf verlassen, dass die Daten unverändert in der CSV Datei landen. Und wenn dort nur ein CR oder LF enthalten ist, dann kommt auch nur das heraus - und zwar 24 Stunden an allen 7 Tagen jeder Woche. Der Zeilenumbruch mit CRLF ist Teil der Struktur und nicht der Daten. Ich denke aber wir haben eine unterschiedliche Lesart der RFC. Meine deckt sich mit dem größten Teil der Entwickler und deine ... ist deine Sache. |
AW: csv Datei Import ClassHelper für TClientDataSet
https://tools.ietf.org/html/rfc4180
Ich vermute das #10 wird von Blackpit mißinterpretiert. Gruß K-H |
AW: csv Datei Import ClassHelper für TClientDataSet
Zitat:
Das als Grundlage: Zitat:
Apple systems define sLineBreak as simply a carriage return (#13). Gruß BP |
AW: csv Datei Import ClassHelper für TClientDataSet
Die ganze Diskussion ist hinfällig, da es keine einheitliche CSV Format gibt.
Zitat:
Excel verwendet z.B. diese Ansi Norm. Mach mal einen Doppelklick auf eine mit Komma getrennte csv Datei. ;) Es macht also keinen Sinn hier über Implementierungsdetails streiten. Folgendes Beispiel ist auch NICHT rfc konform:
Code:
(Das "ß" ist nicht erlaubt.)
MaBuSE,"Hauptstraße 5",12345,Musterstadt
Das Zeilenwechsel Problem ist ja auch kein CSV spezifisches Problem. Das gibt es grundsätzlich beim Austausch von Textdateien zwischen PC <-> Unix <-> alte Mac. Ein Zeilenwechsel kann sein #13#10, #10 oder auch #13 Letzteres war früher bei Macs, ist aber mittlerweile unüblich. TStringList kann auf PC vernünftig nur mit #13#10 umgehen. Je nachdem wie man die Daten zuweist, wird es auch nach #13#10 umgewandelt oder nicht. :shock: Das ist aber auch schon ewig so. Ich habe das nach dem Laden/Zuweisen immer mit einem
Delphi-Quellcode:
gelöst. Damit werden alle #13 und #10 zu #13#10 umgewandelt.
sl.Text := sl.Text;
Hier zum Testen: (Leeres Form mit einem TButton und 5 TMemo)
Delphi-Quellcode:
-> so sieht es dann aus:
procedure TForm1.Button1Click(Sender: TObject);
var sl: TStringList; begin sl := TStringList.Create; sl.Add('Aa,Bb'); sl.Add('"A'#13#10'a",Bb'); sl.Add('"A'#10'a",Bb'); sl.Add('"A'#13'a",Bb'); Memo1.Lines.Text := sl.Text; // mit Umwandlung von #13 und #10 nach #13#10 Memo2.Lines.Add(sl.Text); // ohne Umwandlung Memo3.Lines.Text := s2hex(Memo1.Lines.Text); Memo4.Lines.Text := s2hex(Memo2.Lines.Text); Memo5.Lines.Text := s2Hex(sl.Text); end; function TForm1.s2hex(s: string): string; var i: Integer; begin for i := 1 to length(s) do begin Result := Result + IntToHex(Ord(s[i]),2) + #32; end; end;
Code:
Memo1:
Aa,Bb "A a",Bb "A a",Bb "A a",Bb Memo2: Aa,Bb "A a",Bb "Aa",Bb "Aa",Bb 41 61 2C 42 62 0D 0A 22 41 0D 0A 61 22 2C 42 62 0D 0A 22 41 0A 61 22 2C 42 62 0D 0A 22 41 0D 61 22 2C 42 62 0D 0A // sl.Text 41 61 2C 42 62 0D 0A 22 41 0D 0A 61 22 2C 42 62 0D 0A 22 41 0D 0A 61 22 2C 42 62 0D 0A 22 41 0D 0A 61 22 2C 42 62 0D 0A // sl.Text nach Text := Text 41 61 2C 42 62 0D 0A 22 41 0D 0A 61 22 2C 42 62 0D 0A 22 41 0D 0A 61 22 2C 42 62 0D 0A 22 41 0D 0A 61 22 2C 42 62 0D 0A // Memo1.Lines.Text 41 61 2C 42 62 0D 0A 22 41 0D 0A 61 22 2C 42 62 0D 0A 22 41 0A 61 22 2C 42 62 0D 0A 22 41 0D 61 22 2C 42 62 0D 0A 0D 0A // Memo2.Lines.Text mit zusätzlichem #13#10 am Ende (durch das Add) |
AW: csv Datei Import ClassHelper für TClientDataSet
Zitat:
Code:
Beachte die " mit denen die Felder die speziellen Zeichen einrahmen.
Zeile1: a;b;c;d
Zeile2: a;"b;b";"c#10c";d Zeile1: a b c d Zeile2: a "b;b" "c#10c" d Speziell sind in diesem Fall das ; und #10 Gruß K-H |
AW: csv Datei Import ClassHelper für TClientDataSet
Ich wollte mit meinem Beitrag eigentlich die unsinnige Diskussion über #13#10 und #10 beenden.
Falls ihr es nicht gemerkt habt. Das ist ein Code-Bibliothek Beitrag. Hier sollte nicht so viel diskutiert werden. Macht einfach nach dem:
Delphi-Quellcode:
ein
slFile.LoadFromFile( Filename );
Delphi-Quellcode:
und beendet diese Diskussion hier.
slFile.Text := slFile.Text
(Oder macht einen eigenen Thread mit dem Titel "unsinnige Diskussion über #13#10 und #10 im Kontext des rfc4180" außerhalb der Code-Library auf.) Danke |
AW: csv Datei Import ClassHelper für TClientDataSet
Zitat:
Delphi-Quellcode:
HTH
procedure TClientDataSetCsvClassHelper.LoadFromFile;
var myString :Variant; slFile :TStringList; slRow :TStringList; x,i,j :Integer; myPos :Integer; myFields :Integer; cntFields :Integer; begin slFile := TStringList.Create; slRow := TStringList.Create; try myPos := 0; slRow.Delimiter := Delimiter; slRow.QuoteChar := QuoteChar; slRow.StrictDelimiter := True; slFile.StrictDelimiter := True; slFile.LoadFromFile( Filename ); // ClientDataset Initialisieren if slFile.Count > 0 then begin Active := False; for x := 0 to slFile.Count - 1 do begin myString := ''; if (x = 0) then begin myPos := x; // Build Header for DS slRow.DelimitedText := slFile[0]; if FirstLineTitle then myFields := slRow.Count else begin cntFields := 0; myString := slFile[myPos]; if Odd(length( myString )- length( stringreplace( myString, QuoteChar,'',[rfreplaceall, rfIgnoreCase]))) then while Odd(length( myString )- length( stringreplace( myString, QuoteChar,'',[rfreplaceall, rfIgnoreCase]))) do begin Inc(mypos); myString := myString+slFile[myPos]; cntFields := length( myString )- length( stringreplace( myString, Delimiter,'',[rfreplaceall, rfIgnoreCase])); end else myFields := slRow.Count; if cntFields > 0 then myFields := cntFields + 1; FieldDefs.Clear; for i := 0 to myFields - 1 do begin FieldDefs.Add( Format( 'Field%d',[i]), ftWideString, StringLength ); end; end; if FirstLineTitle then begin for i := 0 to myFields - 1 do FieldDefs.Add( slRow[i], ftWideString, StringLength ); end; CreateDataSet; Active := True; cntFields := 0; end; begin if (x=0) and FirstLineTitle then inc( myPos ); if myPos = slFile.Count then break; If myString = '' then myString := slFile[myPos]; cntFields := length( myString )- length( stringreplace( myString, Delimiter,'',[rfreplaceall, rfIgnoreCase])); while cntFields <= (myFields-1) do if FirstLineTitle and (cntFields < (myFields-1)) then begin inc( myPos ); myString := myString+slFile[myPos]; cntFields := length( myString )- length( stringreplace( myString, Delimiter,'',[rfreplaceall, rfIgnoreCase])); end else if not FirstLineTitle and (x <> 0) then begin If myString = '' then myString := slFile[myPos]; while Odd(length( myString )- length( stringreplace( myString, QuoteChar,'',[rfreplaceall, rfIgnoreCase]))) do if Odd(length( myString )- (length( stringreplace( myString, QuoteChar,'',[rfreplaceall, rfIgnoreCase])))) then begin inc( myPos ); myString := myString+slFile[myPos]; cntFields := length( myString )- length( stringreplace( myString, Delimiter,'',[rfreplaceall, rfIgnoreCase])); end else cntFields := length( myString )- length( stringreplace( myString, Delimiter,'',[rfreplaceall, rfIgnoreCase])); end else break; end; // fill TClientDataset DisableControls; begin slRow.DelimitedText := myString; Append; for j := 0 to slRow.Count - 1 do begin Fields[j].AsString := slRow[j]; end; Post; end; EnableControls; inc( myPos ); end; end; finally slFile.Free; slRow.Free; end; end; |
AW: csv Datei Import ClassHelper für TClientDataSet
Doppelpost bitte löschen
|
AW: csv Datei Import ClassHelper für TClientDataSet
Doppelpost bitte löschen
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:28 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz