UTF-8 Gleichheitsoperator Bug?

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
HHick123
Beiträge: 21
Registriert: Mo 10. Nov 2014, 00:28

Re: UTF-8 Gleichheitsoperator Bug?

Beitrag von HHick123 »

Ok, ich hab' nun wieder eine Version gemacht, die den Vergleich der beiden UTF-8-Strings, der "ungleich" ergibt durchführt (anbei) - quasi das Problem aus meinem Ursprungsposting:

mit Codetyphon 5.5/3.1.1:
Ausgabe:
wärme
wärme
6
6
5
5
65001
0
ungleich

Der eine UTF-8-String liefert in diesem Fall tatsächlich einen anderen Wert für StringCodePage, nämlich 65001 anstelle von 0....

Edit:
mit Codetyphon 5.
7/3.1.1 ergibt sich nun:
Ausgabe:
wärme
wärme
6
6
5
5
65001
0
gleich
Dateianhänge
20160310b.zip
(204.97 KiB) 47-mal heruntergeladen
Zuletzt geändert von HHick123 am Fr 11. Mär 2016, 14:02, insgesamt 8-mal geändert.

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

Re: UTF-8 Gleichheitsoperator Bug?

Beitrag von wp_xyz »

Wenn die CodePage gesetzt wird, brauchst du nichts mehr konvertieren, das macht fpc automatisch, weil die Code-Pages ja jetzt bekannt sind.

Michl, was ich jetzt nicht verstehe ist, ist warum AnsiToUtf8 unbedingt entfernt werden muss, ich war der festen Überzeugung, das wäre jetzt wirkungslos...

So geht's jedenfalls:

Code: Alles auswählen

procedure Initialize;
var
 rs:rawbytestring;
 sb,sc:string;
 l:TListEnumerator;
begin
 setlength(dictionary,0);
 assignfile(f,'KKS.csv');
 reset(f);
 while not(eof(f)) do
 begin
  readln(f,rs);
  SetCodePage(rs,1252,false);
//  sb:=AnsiToUTF8(rs);
  sb := rs;
  l.Init(sb,';');                                   //Breakpoint 1
//  sc:=AnsiToUTF8(l.NextItem);
  sc := L.NextItem;
  setlength(dictionary,length(dictionary)+1);       //Breakpoint 2
  dictionary[length(dictionary)-1].GermanLong:=sc;
 
 end;
 closefile(f);
end

Ich persönlich finde aber die Version mit expliziter Umwandlung, ohne das SetCodePage, besser - da weiß man direkt, was passiert, und man kann durchgängig mit "string" arbeiten und muss nicht auf den "rawbytestring" ausweichen.

Code: Alles auswählen

procedure Initialize;
var
 rs,sb,sc:string;
 l:TListEnumerator;
begin
 setlength(dictionary,0);
 assignfile(f,'KKS.csv');
 reset(f);
 while not eof(f) do
 begin
   readln(f, rs);
   sb := WinCPToUTF8(rs);
   l.Init(sb, ';');
   sc := l.Nextitem;
   setlength(dictionary,length(dictionary)+1);
   dictionary[length(dictionary)-1].GermanLong:=sc;
  end;
  closefile(f);
end;

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

Re: UTF-8 Gleichheitsoperator Bug?

Beitrag von Michl »

Sorry, hab da einen Denkfehler gehabt, da ich nur das Endergebnis schnell getestet hatte. Die Stringmagic funktioniert nur mit vordefinierten Strings (siehe http://wiki.freepascal.org/FPC_Unicode_support#Static_code_page, wobei der Begriff "Static" ungenau ist und durch "Vorbelegt" in der offiziellen Dokumentation ersetzt wurde, ist aber ein anderes Thema).

So wäre es richtig:

Code: Alles auswählen

procedure Initialize;
var
 rs: RawByteString;
 sb, sc:string;
 l:TListEnumerator;
begin
 setlength(dictionary,0);
 assignfile(f,'KKS.csv');
 reset(f);
 while not(eof(f)) do
 begin
  readln(f, rs);
  SetCodePage(rs, 1252, False);
  SetCodePage(rs, CP_UTF8, True)// Diese Zeile einfügen
  sb := rs;
//  sb:=AnsiToUTF8(rs);
  l.Init(sb,';');                                   //Breakpoint 1
//  sc:=AnsiToUTF8(l.NextItem);
  sc:=l.NextItem;
 
  setlength(dictionary,length(dictionary)+1);       //Breakpoint 2
  dictionary[length(dictionary)-1].GermanLong:=sc;
 
 end;
 closefile(f);
end;

Eine Stringkonstante im Quelltext wird mit der Codepage CP_ACP initialisiert, siehe z.B.: http://wiki.freepascal.org/FPC_Unicode_support#Code_page_identifiers.
Da du einen Ansistring einliest, Lazarus einen Hack macht und intern immer UTF8 kodierte Strings verwendet (siehe http://wiki.freepascal.org/Better_Unicode_Support_in_Lazarus#Usage_in_Lazarus), ist es das Einfachste, gleich die Kodierung des zu verarbeitenden Strings zu UTF8 zu ändern und damit weiter zu arbeiten. Daher sind die Ausgaben richtig.

Wie CT mit Strings verfährt, weiß ich nicht. Scheinbar machen sie diesen Hack nicht und man muss sich selber um die entsprechende Kodierung kümmern.

wp_xyz hat geschrieben:Michl, was ich jetzt nicht verstehe ist, ist warum AnsiToUtf8 unbedingt entfernt werden muss, ich war der festen Überzeugung, das wäre jetzt wirkungslos...
Es wird UTF8Encode aufgerufen. Allerdings ist es tatsächlich wirkungslos, wenn eine schon UTF8 kodierter String übergeben wird, daher nicht notwendig (wenn man nicht, wie ich, vergisst die CodePage des Strings nach UTF8 zu ändern).

Code: Alles auswählen

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

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

Re: UTF-8 Gleichheitsoperator Bug?

Beitrag von Michl »

Vielleicht noch zur Ergänzung:

Dieser Test schlägt fehlt, da die Ausgangskodierung nicht bekannt ist:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
const
  StrCP1252 = #$80#$C4#$D6#$8C#$A5;
var
  rs: RawByteString;
  s: String;
begin
  rs := StrCP1252;
  s := AnsiToUtf8(rs);
  Caption := IntToStr(StringCodePage(s)) + ' ' + s;
end;

Daher muss man diese definieren:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
const
  StrCP1252 = #$80#$C4#$D6#$8C#$A5;
var
  rs: RawByteString;
  s: String;
begin
  rs := StrCP1252;
  SetCodePage(rs, 1252, False);
  s := AnsiToUtf8(rs);
  Caption := IntToStr(StringCodePage(s)) + ' ' + s;
end;

Nun kann man aber auch gleich die CodePage des Strings identisch ändern, ohne AnsiToUTF8 oder dgl:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
const
  StrCP1252 = #$80#$C4#$D6#$8C#$A5;
var
  rs: RawByteString;
  s: String;
begin
  rs := StrCP1252;
  SetCodePage(rs, 1252, False);
  SetCodePage(rs, CP_UTF8, True);
  s := rs;
  Caption := IntToStr(StringCodePage(s)) + ' ' + s;
end;

So gänge es auch:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
const
  StrCP1252 = #$80#$C4#$D6#$8C#$A5;
var
  s: String;
begin
  s := StrCP1252;
  SetCodePage(RawByteString(s), 1252, False);
  SetCodePage(RawByteString(s), CP_UTF8, True);
  Caption := IntToStr(StringCodePage(s)) + ' ' + s;
end

Code: Alles auswählen

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

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

Re: UTF-8 Gleichheitsoperator Bug?

Beitrag von wp_xyz »

Und warum geht das nicht (ich bleibe bei dem von HHick123 geposteten Beispiel)? Hier wird rs als String für die Codepage 1252 deklariert, dann müsste bei "s := rs" doch die Umwandlung nach UTF8 richtig werden - was aber nicht so ist:

Code: Alles auswählen

procedure Initialize;
type
   CP1252String = type Ansistring(1252);   // <------
var
  rs: CP1252String;    // <-----
  s: String;
  l: TListEnumerator;
begin
  SetLength(dictionary, 0);
  assignFile(f, 'KKS.csv');
  reset(f);
  while not eof(f) do
  begin
    readln(f, rs);
    s := rs;
    l.Init(s, ';');
    setlength(dictionary,length(dictionary)+1);
    dictionary[length(dictionary)-1].GermanLong:=l.NextItem;
  end;
  closefile(f);
end;
 

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

Re: UTF-8 Gleichheitsoperator Bug?

Beitrag von Michl »

Ich habe mich sehr ausgiebig mit den Strings befasst (an die hundert Tests habe ich noch rumliegen) und kenne noch einige unveröffentliche Bugs mit ACP-Strings unter Windows, muss aber auch immer wieder spezielle Verhalten in kleinen Tests gegentesten. Trotzdem versuche ich es mal:

Lazarus macht bei der Verwendung der LCL unter Windows einen Hack und definiert die Defaultsystemcodepage als UTF8. Eigentlich ist das falsch, da normalerweise eine länderspezifische Ansicodepage vom System verwendet wird (man will längerfristig auf Widestrings umswitchen, diese Lösung stellt daher nur ein Zwischenschritt für die nächsten ... Jahre dar).
Nicht desto trotz wird eine String-Variable mit 0 (CP_ACP, der Default-Ansicodepage) vorbelegt. Eine Zuweisung eines Strings zu einem anderen String ohne vordefinierter Codepage (CP_ACP) führt zur Kopie des Strings, nicht zur Umwandlung. Das kann man z.B. in dem zuvor geposteten Code so testen:

Code: Alles auswählen

  WriteLn(StringRefCount(rs));
  s := rs;
  WriteLn(StringRefCount(rs));
Es wird der Referenzzähler des Strings hochgezählt.

Die Codemagic kann man nutzen, wenn man Strings mit vordefinierter Codepage verwendet:

Code: Alles auswählen

procedure Initialize;
type
  CP1252String = type Ansistring(1252);
var
  cs: CP1252String;
  s: String;
  su: UTF8String;
  l: TListEnumerator;
begin
 setlength(dictionary,0);
 assignfile(f,'KKS.csv');
 reset(f);
 while not(eof(f)) do
 begin
  readln(f, s);
  SetCodePage(RawByteString(s), 1252, False);
  cs := s;
  WriteLn(cs, StringRefCount(cs));
  su := cs;
  WriteLn(su, StringRefCount(cs));
  l.Init(su,';');
  setlength(dictionary,length(dictionary)+1);
  dictionary[length(dictionary)-1].GermanLong:=l.NextItem;;
 end;
 closefile(f);
end;

MMn viel verwirrender ist, dass z.B. ein UTF8String auch jede andere Codepage bei entsprechender Zuweisung erhalten kann. Dazu hatte ich mal einen Bugreport http://bugs.freepascal.org/view.php?id=29651 gemacht. Daher wäre meine Empfehlung sämtliche mit Ansicodepagen vordefinierten Stringtypen, wie den UTF8String zu entfernen, da sie nicht halten, was sie versprechen.

Dass ein ReadLn(f, rs) direkt in eine CP1252 definierten String nicht funktioniert (in deinem Beispiel), liegt an der Definition der DefaultFileSystemCodepage (der vermeintliche eingelesene UTF8-String wird dabei per Stringmagic in einen CP1252 konvertiert und dies führt zu dem Fragezeichen).

Warum das Ganze? Das kann ich leider nicht beantworten. Die einzige Erklärung, die mir bei diversen Bugreports immer wieder genannt wurde, ist, dass Delphi sich genau so verhält. Eigentlich sind alle Besonderheiten mit den neuen Strings auf dieser Seite beschrieben: http://wiki.freepascal.org/FPC_Unicode_support. Leider versteht man die Sätze mit ihrer Tragweite zumeist erst, wenn man praktische Probleme hat.

Code: Alles auswählen

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

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

Re: UTF-8 Gleichheitsoperator Bug?

Beitrag von wp_xyz »

Danke, das ist alles sehr verwirrend... Ich lerne daraus: Beim Umgang mit Dateien fühle ich mich darin bestätigt, einfach mit "string" zu arbeiten und die entsprechenden Konvertierungsroutinen aufzurufen, anstatt auf die schlecht dokumentierten Tricks des Compilers zu vertrauen, zumal das durch die SetCodePage-Aufrufe auch nicht kürzer wird. Aus diesem Grund ist auch die Umstellung von fpc von v2.6.4 auf 3.0 an fpspreadsheet ohne nötige Änderungen vorübergegangen (und da gibt es in den alten Excel-Formaten eine Menge in Ansi).

HHick123
Beiträge: 21
Registriert: Mo 10. Nov 2014, 00:28

Re: UTF-8 Gleichheitsoperator Bug?

Beitrag von HHick123 »

Hallo,
noch ein Nachtrag von meiner Seite:

Nachdem ich nun doch etwas verzweifelt war mit CT (vorallem hatte ich noch ein zweites Problem, das ich nur kurz in einem meiner obigen Postings ansprach: Nämlich, dass ich die UTF-8-Codierung immer wieder "verlor", d.h. sie waren wieder ANSI und das passierte, wie sich zeigte z.B. unter bestimmten Umständen, wenn ich sie mit dem "+"-Operator zusammenfügte, und das ist ja nicht gerade eine seltene Sache...), hab' ich nun CT5.7 (welches vor ein paar Tager released wurde) probiert. Anscheinend wurde tatsächlich etwas gemacht (oder es lag vorher an meinen Einstellungen der 5.5er-Version). Das Verhalten dürfte nun bezüglich der Thematik dieses Threads, soweit ich das gesehen habe, Lazarus 1.6 entsprechen. :-)

Entsprechende Edits hab' ich in meine Postings oben eingefügt.

Michl hat geschrieben:Gesteinigt wirst du nicht, musst nur zehn Liegestütze zur Strafe machen :mrgreen:


mschnell hat geschrieben:Das ist kein Bug von Lazarus.


Ja, ok, ich mach nun freiwillig 20 Liegestütz. Mit Lazarus wär' ich vermutlich schneller am Ziel gewesen.
Zumal im Lazarusforum ;-)

Denn im nachhinein denke ich, es war vorallem ein Problem von CT 5.5 (oder vielleicht auch nur meiner Compiler-Einstellungen der CT5.5-Installation?!)

Habe jedenfalls wieder einiges über strings gelernt...
Danke für die Unterstützung

LG Helmut

Antworten