Stringlist mit Umlauten in CSV abspeichern

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
dash_mc
Beiträge: 4
Registriert: Di 8. Mär 2016, 14:47

Stringlist mit Umlauten in CSV abspeichern

Beitrag von dash_mc »

Hallo,

schonmal vorab: ich bin kein gelernter Programmierer und programmiere auch nur ab und zu mal etwas :?
Leider bin ich im Moment etwas am verzweifeln, habe schon etliche Foreneinträge durchforstet und Lösungsversuche hinter mir, das Problem bekomme ich jedoch nicht in den Griff. Ich programmiere
derzeit in Lazarus 1.6 (x86) unter Windows 10 ein kleines Tool, das mir mehrere Strings aus einer SQLite-DB ausliest, die dann in eine CSV abgespeichert werden. Ein externes Etikettentool wird dann per Shellexecute aufgerufen, das dann auf
die CSV Datei zugreift und im Anschluss ein Etikett mit Barcode druckt. Soweit so gut.
Problem dabei ist, das die CSV beim abspeichern (laut Notepad++) als UTF-8 abgespeichert wird und das Etikettentool die vorhandenen Umlaute dann eben versemmelt (z.B. ein "ü" wird zu "ü" usw.).
Sehe ich mir die CSV im Editor an, ist das "ü" auch noch vorhanden, in Excel schon nichtmehr.. :x



Hier der Quellcode:

Code: Alles auswählen

procedure TForm_Hauptformular.BitBtn_DruckenClick(Sender: TObject);
 var sl    : TStringList;
     i     : Integer;
     s     : String;
begin
  sl := TStringList.Create;
  sl.Clear;
  s  :='';
 
  try
    SQLQuery1.Close;
    SQLQuery1.SQL.Text:=Concat('SELECT * FROM tblEtiketten WHERE art_nummer ='+ComboBox_Artikelnummer.Text);
    SQLQuery1.Open;
    while not SQLQuery1.EOF do begin
     s:=SQLQuery1.Fields[0].AsString+','+SQLQuery1.Fields[1].AsString+','+SQLQuery1.Fields[2].AsString+','+SQLQuery1.Fields[3].AsString+','+SQLQuery1.Fields[4].AsString+','+SQLQuery1.Fields[5].AsString+','+SQLQuery1.Fields[6].AsString+','+SQLQuery1.Fields[7].AsString;
     SQLQuery1.Next;
    end;
    sl.Add('art_nummer,art_text_1,art_text_2,art_text_3,kunde,menge,gebinde,satz');
    for i:= 0 to StrToInt(SpinEdit_Anzahl.Text) -1 do begin
     sl.Add(s);
    end;
    sl.SaveToFile(CSV_Pfad);
  finally
    sl.Free;
  end;
 
  s := ComboBox_Artikelnummer.Text;
  ShellExecute(0,'open','tfprint.exe',concat('-F ',EtikettenVorlage,' -O "',Druckername,'"'),nil,SW_HIDE);
end; 

wp_xyz
Beiträge: 5228
Registriert: Fr 8. Apr 2011, 09:01

Re: Stringlist mit Umlauten in CSV abspeichern

Beitrag von wp_xyz »

Zunächst mal: Dein Programm liest einen Record in einen String ein, geht zum nächsten Record, liest ihn wieder in denselben (!) String usw. Das heißt, wenn du am ende die Stringliste füllst, hast du nur noch den letzten String. Ist das so beabsichtigt? Ich denke eher, dass du die Titelzeile der Stringliste schon nach dem Erzeugen der Stringliste füllen musst, und das "Sl.Add(s)" in die While-Schleife gehört, unmittelbar nach dem Zusammenbauen von s aus den Feldinhalten.

Nun zu deinem Problem: Ich denke, das Problem liegt in dem Labeldruckprogramm, es kommt offenbar mit den UTF8-Strings, die ihm dein Programm liefert, nicht klar. Ich kenne natürlich dieses Programm nicht, aber wenn es nicht zu alt ist, erwartet es wahrscheinlich Unicode-Strings (UTF-16). Das heißt, du musst die Strings (s), die du aus den Datenbankfeldern zusammenbaust, zuerst nach Unicode umwandeln, bevor du sie in die Stringliste hängst; dafür kannst du z.B. UTF8Decode(s) verwenden. (Oder vielleicht gibt es auch einen Abfrageparameter für Sqlite für das gewünschte Encoding).

Also:

Code: Alles auswählen

procedure TForm_Hauptformular.BitBtn_DruckenClick(Sender: TObject);
var 
  sl: TStringList;
  i: Integer;
  s: String;
begin
  sl := TStringList.Create;
  try
    //sl.Clear;  // unnötig - die Liste ist nach "Create" automatisch leer.
    sl.Add('art_nummer,art_text_1,art_text_2,art_text_3,kunde,menge,gebinde,satz');
    SQLQuery1.Close;
    SQLQuery1.SQL.Text:=Concat('SELECT * FROM tblEtiketten WHERE art_nummer ='+ComboBox_Artikelnummer.Text);
    SQLQuery1.Open;
    while not SQLQuery1.EOF do begin
      s:=SQLQuery1.Fields[0].AsString+','+
         SQLQuery1.Fields[1].AsString+','+
         SQLQuery1.Fields[2].AsString+','+
         SQLQuery1.Fields[3].AsString+','+
         SQLQuery1.Fields[4].AsString+','+
         SQLQuery1.Fields[5].AsString+','+
         SQLQuery1.Fields[6].AsString+','+
         SQLQuery1.Fields[7].AsString;
      sl.Add(UTF8Decode(s));
      SQLQuery1.Next;
    end;
    sl.SaveToFile(CSV_Pfad);
  finally
    sl.Free;
  end;
 
  s := ComboBox_Artikelnummer.Text;
  ShellExecute(0,'open','tfprint.exe',concat('-F ',EtikettenVorlage,' -O "',Druckername,'"'),nil,SW_HIDE);
end;

dash_mc
Beiträge: 4
Registriert: Di 8. Mär 2016, 14:47

Re: Stringlist mit Umlauten in CSV abspeichern

Beitrag von dash_mc »

Moin,

schonmal vielen Dank. Das mit dem Einlesen der Daten aus der Datenbank ist genau so gewollt, da das Labelprogramm pro Zeile in der CSV ein Etikett druckt. Etwas verwirrend, aber die Syntax von dem Programm ist halt so.
Die oberste Zeile (art_nummer, art_text_1 etc.) steuert im Programm, wo der jeweilige Text positioniert ist.

So:
Habe meinen Code mal etwas aufgeräumt bzw. entrümpelt und den String per UTF8Decode in die Stringlist eingelesen. Beim starten kommt dann schon eine Warnung:
Warning: Implicit string type conversion with potential data loss from "UnicodeString" to "AnsiString"
Die jetzt neu erzeugte CSV wird in Notepad allerdings immer noch als UTF-8 Codiert erkannt und auch im neu erzeugten Label werden die Umlaute nicht erkannt.

Erstelle ich manuell in Notepad eine CSV in der Umlaute enthalten sind, wird diese als ANSI-Codiert und das Label wird korrekt gedruckt.
Weitere Ideen?

Code: Alles auswählen

procedure TForm_Hauptformular.BitBtn_DruckenClick(Sender: TObject);
 var sl    : TStringList;
     i     : Integer;
     s     : String;
begin
  sl := TStringList.Create;
  try
    SQLQuery1.Close;
    SQLQuery1.SQL.Text:=Concat('SELECT * FROM tblEtiketten WHERE art_nummer ='+ComboBox_Artikelnummer.Text);
    SQLQuery1.Open;
    while not SQLQuery1.EOF do begin
     s:=SQLQuery1.Fields[0].AsString+','+
        SQLQuery1.Fields[1].AsString+','+
        SQLQuery1.Fields[2].AsString+','+
        SQLQuery1.Fields[3].AsString+','+
        SQLQuery1.Fields[4].AsString+','+
        SQLQuery1.Fields[5].AsString+','+
        SQLQuery1.Fields[6].AsString+','+
        SQLQuery1.Fields[7].AsString;
     SQLQuery1.Next;
    end;
    sl.Add('art_nummer,art_text_1,art_text_2,art_text_3,kunde,menge,gebinde,satz');
    for i:= 0 to StrToInt(SpinEdit_Anzahl.Text) -1 do begin
     sl.Add(UTF8Decode(s));
    end;
    sl.SaveToFile(CSV_Pfad);
  finally
    sl.Free;
  end;
  s := ComboBox_Artikelnummer.Text;
  ShellExecute(0,'open','tfprint.exe',concat('-F ',EtikettenVorlage,' -O "',Druckername,'"'),nil,SW_HIDE);
end; 

wp_xyz
Beiträge: 5228
Registriert: Fr 8. Apr 2011, 09:01

Re: Stringlist mit Umlauten in CSV abspeichern

Beitrag von wp_xyz »

dash_mc hat geschrieben:Erstelle ich manuell in Notepad eine CSV in der Umlaute enthalten sind, wird diese als ANSI-Codiert und das Label wird korrekt gedruckt.
Dann musst du den UTF8-String nach Ansi umwandeln, nicht nach UTF16 wie ich gedacht hatte. Nimm UTF8ToCP1252 aus der Unit lconvencoding (statt UTF8Decode).

P.S.
Die Logik in der Auswertung der SQL-Abfrage verstehe ich immer noch nicht: Das "s", das in die StringListe eingefügt ist, ist dasjenige, das für den letzten Datensatz berechnet wurde; die aus den vorigen Datensätzen wurden jeweils von dem des nächsten Datensatzes überschrieben. Wieso musst du dann überhaupt alle Datensätze durchlaufen? Geh doch gleich zum letzten Datensatz:

Code: Alles auswählen

  SQLQuery1.Open;
  SQLQuery1.Last;
  s := SQLQuery1.Fields[0].AsString+','+  ....
Oder, falls die Abfrage nur 1 Datensatz zurückliefert, weil "art_nummer" eindeutig ist, berechne s doch gleich aus dem Datensatz direkt nach dem Öffnen:

Code: Alles auswählen

  SQLQuery1.Open;
  s := SQLQuery1.Fields[0].AsString+','+...  
Zuletzt geändert von wp_xyz am Mi 9. Mär 2016, 15:13, insgesamt 1-mal geändert.

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Stringlist mit Umlauten in CSV abspeichern

Beitrag von Michl »

Eine StringList mit verschiedenen Kodierungen könntest du z.B. so speichern:

Code: Alles auswählen

{$modeswitch typehelpers}
...
type
 
  { TStringListHelper }
 
  TStringListHelper = class helper for TStringList
    procedure SaveToFileCpAware(FileName: String; CodePage: TSystemCodePage);
    procedure SaveToFileWide(FileName: String);
  end;
...
 
{ TStringListHelper }
 
procedure TStringListHelper.SaveToFileCpAware(FileName: String; CodePage: TSystemCodePage);
var
  FS: TFileStream;
  s: String;
begin
  FS := TFileStream.Create(FileName, fmCreate);
  try
    s := Text;
    SetCodePage(RawByteString(s), CodePage);
    if Length(s) = 0 then Exit;
    FS.WriteBuffer(Pointer(s)^, Length(s));
  finally
    FS.Free;
  end;
end;
 
procedure TStringListHelper.SaveToFileWide(FileName: String);
var
  FS: TFileStream;
  ws: WideString;
begin
  FS := TFileStream.Create(FileName, fmCreate);
  try
    ws := UTF8Decode(Text);
    if Length(ws) = 0 then Exit;
    FS.WriteBuffer(Pointer(ws)^, Length(ws) * 2);
  finally
    FS.Free;
  end;
end;
 
Habe ich so getestet (Windows 7, Lazarus 1.7 Trunk, FPC 3.1.1 Trunk):

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
  SL: TStringList;
begin
  SL := TStringList.Create;
  SL.Text := 'Test' + LineEnding + 'ÄÖÜ' + LineEnding + 'ḂӥԱẄɐ';
  SL.SaveToFile('TestUTF8.csv');
  SL.SaveToFileCpAware('TestCP1252.csv', 1252);
  SL.SaveToFileCpAware('TestCPUTF8.csv', CP_UTF8);
  SL.SaveToFileCpAware('TestCP850.csv', 850);
  SL.SaveToFileWide('TestWide.csv');
  SL.Free;
end;

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

dash_mc
Beiträge: 4
Registriert: Di 8. Mär 2016, 14:47

Re: Stringlist mit Umlauten in CSV abspeichern

Beitrag von dash_mc »

Hallo,

habs jetzt hinbekommen!

Code: Alles auswählen

  SL.SaveToFileCpAware('TestCP1252.csv', 1252);
Das war genau das was ich gesucht habe! Vielen Dank!

Antworten