[gelöst] Fehler bei Stringkonvertierung

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Fehler mit Stringkonvertierung

Beitrag von mschnell »

BeniBela hat geschrieben:Wenn man denkt das sei einfacher als utf-8, verliert man alle Zeichen nach U+10000
Klar. Hab ich auch noch nie gebraucht.

Eine vernünftige Methode ist deshalb, dem Benutzer die Freiheit zu geben, die Codierung zu verwenden, die für seine Anwendung am sinnvollsten ist. (Z.B.: locale-Abhängiges 1-Byte/Zeichen ANSI, UTF8 (1++ Bytes/Zeichen), UTF16 (2++ Bytes/Zeichen), UTF32 (4 Bytes/Zeichen) oder vielleicht auch was ganz anderes u,u. selbst implementiertes).

Dafür wurden in Delphi die "codepage aware ANSIStrings" erfunden (wobei die Bezeichnung ziemlich idiotisch ist, weil meist Unicode verwendet wird und das ist für mich keine "ANSI-Codepage". Aber das ist natürlich Wortklauberei.)

Hier werden die Strings automatisch (theoretisch) immer genau dann umcodiert, wenn es nötig ist. Leider ist die Implementierung in Delpih XE meiner Ansicht nach sehr schlecht, da die API wichtiger mitgelieferter Komponenten (wie TStrings und Abkömmlinge wie TStringlist) dann doch wieder eine bestimmte Codierung erzwingen und durch unnötige Umkodierung hin und her Unmengen Performance klauen, sofern der User eine andere Codierung bevorzugt.

Eine andere Möglichkeiten wären z.B., beim in den Projekt-Optionen eine feste String Codierung zu hinterlegen und das Projekt jeweils entsprechend zu übersetzen, wobei in den Units andere Codierungen nur durch explizite User-Code-Programmierung möglich sind. (TStringList etc müsste dann natürlich auch jeweils entsprechend übersetzt werden.)

Aber das alles löst einige generelle Probleme nicht, die einem bei Unicode leicht das Genick brechen. Beispielsweise sind auch innerhalb desselben Unicode Codierungs-Schemas mehrere unterschiedliche Codierungs-Varianten für dasselbe "sichtbare" Zeichen zulässig. Hierdurch ist ein String-Vergleich auf Gleichheit nicht sauber zu machen. Da diese Varianten u.a. durch Doppel-Zeichen dargestellt werden ist (auch bei UTF32, wo jedes "Zeichen" genau einen "Code" hat) die String-Länge einer vorgegebenen (sichtbaren) Information nicht eindeutig definierbar.

Noch schlimmer wird es bei nicht-Case-Sensitivem Vergleich.

Noch schlimmer wird es bei Vergleich von Strings auf größer/kleiner (zum Sortieren). Dann kommt noch die Code-Reihenfolge zum Tragen, die auch bei Unicode locale - abhängig ist - obwohl der Sinn von Unicode ja eigentlich ist, locale-unabhängige Programmierung zu ermöglichen.

-Michael

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

Re: Fehler mit Stringkonvertierung

Beitrag von Michl »

theo hat geschrieben:
Michl hat geschrieben: Die Frage, warum UTF8toAnsi und dann AnsiToUTF8 einen abweichenden String zum Ausgangsstring liefert, bleibt. Das liegt aber evtl. an meiner FPC-Trunc-Version...
Ich empfehle, sich mit Unicode (UTF.-8, UTF.-16 etc.) etwas auseinanderzusetzen.
Es eignet sich nicht für den Versuch & Irrtum Ansatz. Manchmal scheint es nur zu funktionieren, und dann fliegt man wieder auf die Nase.
Mein Tipp: Besser kurz die Grundlagen checken, als rätseln und sich ärgern.
Wahrscheinlich hast du recht, ich könnte natürlich auch den String selbst zerlegen, das Offset des jeweils letzten Zeichens (evtl. mit Hilfe deiner UTF8Tools) ermitteln und schauen, ob dies ein Buchstabe ist oder nicht. Dies würde Konvertierungen sparen und den möglichen Ärger damit. Eigentlich war dies auch mein ursprüngliches Vorgehen, bis sich durch die Umwandlungen eine scheinbar einfachere Lösung geboten hatte (wird auch offiziell als Möglichkeit empfohlen z.B.: http://wiki.freepascal.org/LCL_Unicode_ ... r_Benutzer).

Wahrscheinlich resultieren die Probleme, die ich derzeit habe aber wirklich aus dem FPC-Trunc, da dort teilweise Sonderzeichen bei UTF8 bis zu 5 Byte einnehmen, was eigentlich nicht sein dürfte?! Die gleichen Zeichen ÄÖÜ in einem UTF8String nehmen bei FPC 2.6.2 zwei Byte ein.

Code: Alles auswählen

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

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Fehler mit Stringkonvertierung

Beitrag von mschnell »

Michl hat geschrieben:.. da dort teilweise Sonderzeichen bei UTF8 bis zu 5 Byte einnehmen, was eigentlich nicht sein dürfte?! Die gleichen Zeichen ÄÖÜ in einem UTF8String nehmen bei FPC 2.6.2 zwei Byte ein.
"Zeichen" (Codepoints) sind in UTF8 - wenn ich mich nicht sehr täusche - 1, 2, 3 oder 4 Byte lang.

"Sichtbare Zeichen" (einen Fachbegriff dafür kenne ich nicht) können in Unicode aber aus zwei (vielleicht auch aus mehr) Codepoints zusammengesetzt werden (Ä zum Beispiel aus einem A und einem Codepoint für darüberzusetzende Punkte - einen einzelnen Codepoint für Ä gibt es aber natürlich auch).

Somit kann man ein "Sichtbares Zeichen" in UTF8 theoretisch mit mehr als 4 Byte darstellen. Ich glaube aber nicht, dass das in Lazarus irgendwie mit Absicht gemacht wird.

-Michael

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Fehler mit Stringkonvertierung

Beitrag von mschnell »

Michl hat geschrieben:Die Frage, warum UTF8toAnsi und dann AnsiToUTF8 einen abweichenden String zum Ausgangsstring liefert, bleibt. Das liegt aber evtl. an meiner FPC-Trunc-Version...
UTF8toAnsi verliert jede Menge Information, die AnsiToUTF8 nicht wieder herstellen kann.

UTF8toAnsi ist also nicht umkehrbar. AnsiToUTF8 keine tatsächliche Umkehr-Funktionn davon.

-Michael

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

Re: Fehler mit Stringkonvertierung

Beitrag von Michl »

mschnell hat geschrieben:UTF8toAnsi ist also nicht umkehrbar. AnsiToUTF8 keine tatsächliche Umkehr-Funktionn davon.
Das verstehe ich. Was ich aber nicht verstehe, warum die Byte, die den String darstellen identisch sind, selbst wenn man den UTF8-String vor bzw. nach der Konvertierung als "Caption" sich anzeigen lässt, alles prima ist. Erst, wenn man den String nach "Außen" schickt es zu diesen Problem kommt (zu 99,9% liegts einfach an meiner FPC-Version!!!).

Mir ist halt nicht klar, wann FPC eine Stringkonvertierung durchführt und wann nicht. Literatur dazu finde ich nicht. Ich kann das nur durch probieren herausfinden und stelle da massive Unterschiede zur FPC-Version 2.6.2 gegenüber der 2.7.1 fest. Ich habe mir eben Lazarus 1.3 r43326 FPC 2.7.1 (Trunc 25858) gebaut, das Problem ist gleich zur 25806! Scheinbar funktionieren die Stringkonvertierungen in 2.7.1 nicht mehr richtig.

Folgender Code:

Code: Alles auswählen

procedure TForm1.FormActivate(Sender: TObject);
var
  S:    String;
  Str:  String;
  AStr: AnsiString;
  UStr: UnicodeString;
  UTStr:UTF8String;
  i:    Integer;
begin
 
  Str:='Ö';
  S:='String['+IntToStr(Length(Str))+']['+Str+']: ';
  for i:=1 to Length(Str) do
    S:=S + '[' + IntToHex(Byte(Str[i]), 2) + '] ';
  Status.Write(S);
 
  AStr:=UTF8ToAnsi(Str);
  S:='AnsiString['+IntToStr(Length(AStr))+']['+AnsiToUTF8(AStr)+']: ';
  for i:=1 to Length(AStr) do
    S:=S + '[' + IntToHex(Byte(AStr[i]), 2) + '] ';
  Status.Write(S);
 
  UStr:=UTF8Decode(Str);
  S:='UnicodeString['+IntToStr(Length(UStr))+']['+UTF8Encode(UStr)+']: ';
  for i:=1 to Length(UStr) do
    S:=S + '[' + IntToHex(Byte(UStr[i]), 2) + '] ';
  Status.Write(S);
 
  UTStr:=Str;
  S:='UTF8String['+IntToStr(Length(UTStr))+']['+UTStr+']: ';
  for i:=1 to Length(UTStr) do
    S:=S + '[' + IntToHex(Byte(UTStr[i]), 2) + '] ';
  Status.Write(S);
 
end; 
gibt bei FPC 2.6.2 eine gültige Ausgabe:
String[2][Ö]: [C3] [96]
AnsiString[1][Ö]: [D6]
UnicodeString[1][Ö]: [D6]
UTF8String[2][Ö]: [C3] [96]

während Trunc-FPC 2.7.1 folgende Ausgabe ausspuckt:
String[2][Ö]: [C3] [96]
AnsiString[1][?]: [D6]
UnicodeString[1][?]: [D6]
UTF8String[5][Ö]: [C3] [83] [E2] [80] [93]

Leider nutze ich diverse andere Features von 2.7.1, daß ich ungern wieder unter 2.6.2 entwickeln wöllt, bin ein bischen verwirrt.

Weiss jemand, was FPC 2.7.1 intern als Stringdatentyp nutzt?! Lt. meinem Bsp. oben müsste das ja UTF8 sein!

Versuche ich aber

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
  Str:UTF8String;
begin
  Str:='Ä';
  Caption:=Str;
end;
funktioniert das nicht, das Zeichen "Ä" wird nicht dargestellt. Erst Str:=UTF8Encode('Ä)'; stellt das "Ä" richtig dar!

Code: Alles auswählen

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

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Fehler mit Stringkonvertierung

Beitrag von mse »

Michl hat geschrieben: funktioniert das nicht, das Zeichen "Ä" wird nicht dargestellt. Erst Str:=UTF8Encode('Ä)'; stellt das "Ä" richtig dar!
Probiere mal {$codepage utf8} zuoberst in der unit. Lazarus funktioniert damit vieleicht nicht mehr.

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

Re: Fehler mit Stringkonvertierung

Beitrag von Michl »

mse hat geschrieben:Probiere mal {$codepage utf8} zuoberst in der unit.
Daumen hoch!!! Damit funktioniert FPC korrekt, auch der vorherige Test funktioniert richtig:
String[2][Ö]: [C3] [96]
AnsiString[1][Ö]: [D6]
UnicodeString[1][Ö]: [D6]
UTF8String[2][Ö]: [C3] [96]

Soweit hatte ich überhaupt noch nicht gedacht, dass man die String-Kodierung selber umstellen kann. Danke dafür :D :D :D

Code: Alles auswählen

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

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Fehler mit Stringkonvertierung

Beitrag von mse »

FPC muss wissen in welcher Codierung die string Konstanten vorliegen um die korrekte Konvertierung vornehmen zu können, entweder durch -Fcutf8 Parameter, einen BOM oder {$codepage utf8}. Lazarus bekommt damit vielleicht Probleme, da Lazarus utf-8 in "AnsiString(CP_ACP)" erwartet.
Zuletzt geändert von mse am Mo 28. Okt 2013, 09:40, insgesamt 1-mal geändert.

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

Re: Fehler mit Stringkonvertierung

Beitrag von Michl »

mse hat geschrieben:FPC muss wissen in welcher Codierung die string Konstanten vorliegen um die korrekte Konvertierung vornehmen zu können, entweder durch -Fcutf8 Parameter, einen BOM oder {$codepage utf8}. Lazarus bekommt damit vielleicht Probleme, da Lazarus utf-8 in "AnsiString(CP_APC)" erwartet.
Ja mein Fehler, bisher hatte ich keine Probleme deswegen und mich darum auch nicht gekümmert. Leuchtet aber ein. Lazarus bzw. mein Projekt läuft erstmal, mal sehen.

Ich habe eben auch mein Ausgangsproblem umgestellt. Das klappt jetzt endlich auch ohne Konvertierung ( nutze einfach UTF8, wie ursprünglich programmiert), alle Probleme sind damit auf einmal verschwunden, freu mich riesig! Vielen Dank nochmal!!!

[Edit] Für mein Projekt stellt das die gewünschte Lösung dar! Das im ersten Beitrag gepostete Minimalbsp. (Konvertierung nach Unicode und wieder zurück) funktioniert allerdings immer noch nicht, scheint eine andere Ursache zu haben (stört mich jetzt aber nicht weiter, da ich ja nicht mehr hin- und herkonvertieren muss).

[Edit]
mse hat geschrieben:einen BOM
Ist das praxisnah, habe ich noch nie verwendet und kannte ich bisher auch noch nicht http://de.wikipedia.org/wiki/Byte_Order_Mark?! Denke, das zieht dann eher noch mehr Probleme nach sich?!

Code: Alles auswählen

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

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

Re: Fehler mit Stringkonvertierung

Beitrag von Michl »

mse hat geschrieben: -Fcutf8
Wo wird denn standartmäßig verwendete der Stringcode eingestellt?

Konnte in Projekteinstellungen, Projekteinstellungen->Kommandozeilenparameter, Werkzeuge->Einstellungen oder auch in fpc.cfg keinen entsprechenden Eintrag (-Fc...) finden oder habe ihn zumindest nicht erkannt. Mich interessiert, was aktuell eingestellt ist, damit ich die Konvertierungsprobleme evtl. nachvollziehen kann.

Code: Alles auswählen

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

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Fehler mit Stringkonvertierung

Beitrag von mse »

Lazarus ist so viel ich weiss darauf angewiesen, dass der Compiler keine Konvertierungen vornimmt, darum kein BOM, -Fcutf8 oder {$codepage utf8}. Vielleicht kann ein Lazarus-Spezialist mehr dazu sagen.
Bei MSEgui ist -Fcutf8 standard in den Projekt-templates.

Benutzeravatar
theo
Beiträge: 10900
Registriert: Mo 11. Sep 2006, 19:01

Re: Fehler mit Stringkonvertierung

Beitrag von theo »

mse führt einen gerne mal aufs Glatteis, weil der nämlich Lazarus schlecht reden und sein msegui "verkaufen" will. Vorsicht! :lol:
http://wiki.freepascal.org/LCL_Unicode_ ... _codepages

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Fehler mit Stringkonvertierung

Beitrag von mse »

Dann ist
mse hat geschrieben:Lazarus ist so viel ich weiss darauf angewiesen, dass der Compiler keine Konvertierungen vornimmt
also korrekt?

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

Re: Fehler mit Stringkonvertierung

Beitrag von Michl »

theo hat geschrieben:mse führt einen gerne mal aufs Glatteis, weil der nämlich Lazarus schlecht reden und sein msegui "verkaufen" will. Vorsicht! :lol:
Ist ja auch eine riesige Arbeit, die dahinter steckt und ich kann das daher sehr gut verstehen. Mir haben seine Hinweise auf jeden Fall geholfen, das Grundproblem etwas besser zu verstehen und bin ihm sehr dankbar dafür!
theo hat geschrieben:http://wiki.freepascal.org/LCL_Unicode_ ... _codepages
Das Bsp. trifft den Kern meines Problems.

Ich habe jetzt in meinem Prog. alle {$codepage utf8} entfernt und jetzt funktionieren die UTF8-Strings (sprich der Test, ob das jeweils erste bzw. letzte Zeichen ein Buchstabe ist oder nicht) ordentlich. Weiss nicht, ob das an der anderen FPC-Trunc-Version lag?!

Code: Alles auswählen

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

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Fehler mit Stringkonvertierung

Beitrag von mschnell »

mse hat geschrieben:Lazarus utf-8 in "AnsiString(CP_APC)" erwartet.
Dass die Strings, die Unicode enthalten, irreführender Weise "ANSIString" heißen (sowas ist in einer Umstellungs-Phase ja schlecht zu vermeiden) wusste ich ja. Dass man aber auch noch explizit eine irreführende Codierung ("CP_ACP" = ANSI codepage) angeben muss, war mir neu.

-Michael

Antworten