Ver(schlimm)bessertung für String-Handling (Unicode etc)

Für allgemeine Fragen zur Programmierung, welche nicht! direkt mit Lazarus zu tun haben.
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

Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von mschnell »

Hi alle,

Im (Englischen) Free Pascal Developer's Forum in Thread "utf-8 package in Free Pascal" wurde diskutiert, dass Free Pascal jetzt auf die Delphi "NewStrings", die dynamisch eine Kennung für Kodierung des Strings enthalten und dadurch eine automatische Konvertierung für die verschiedenen Kodierungen (z.B. locale_based_ANSI <-> Unicode und UTF-8 <> UTF-16) erlaubt.

Lazarus wird das wohl übernehmen, wenn FPC es in nächsten stabilen FPC-Release ( 2.8 ) aufgenommen ist.

Hierdurch ändert sich die Stringverarbeitung wieder drastisch (wie von der alten ANSI-Version zur aktuellen UTF-8-Version mit "pseudo-Unicode" handling (UTF-8 code in einem String-Type der ANSIString heißt).

Warten wir ab, Ob es dadurch besser oder schlechter wird. Meine Kollegen berichten jedenfalls von einigen Horror-Stories beim Umstieg auf Delphi Unicode NewStrings. Es wird aber in jedem Fall ähnlicher zur alten ANSI Methodik als zur aktuellen "pseudo-Unicode" Methodik.

-Michael

Patito
Beiträge: 203
Registriert: Di 22. Sep 2009, 13:08
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von Patito »

Klingt problematisch...

Bei Int64, Int32, ... ist auch noch keiner auf die Idee gekommen das alles
in einen einzigen "magischen" Typ wursten zu wollen.

Einer der Hauptvorteile von Pascal war eigentlich immer, dass es weniger automatischen Unsinn als bei C gab...

MmVisual
Beiträge: 1581
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 4 FPC 3.2.2)
CPU-Target: 32/64Bit

Re: Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von MmVisual »

Wenn der Typ String im Speicher nicht nur die Länge sondern auch die Codierung beinhaltet, dann wüsste das der Compiler immer von alleine. Ich denke das ist das Hauptproblem.

In Delphi XE gibt es z.B. String oder AnsiString. Die Konvertierung ist auch ganz einfach:
s: String;
sa: AnsiString = "askdüööß";
s := String(sa);

Ganz ohne UTF8Decode oder UTF8Encode und so einen Umstand.

Ich wäre auch dafür, dass endlich der String-Typ UTF8 wird. Vollständig! So dass auch Length() die richtige Anzahl Zeichen ausspuckt und nicht die Anzahl der Bytes im Speicher.

Welche Probleme sind zu erwarten?
Ganz einfach, jeder der Strings in ein TStrem abgespeichert hat, der muss neu beim wieder einlesen das erst mal als AnsiString lesen und dann konvertieren.

Neu braucht es natürlich auch eine Funktion sizeof() mit der die echte Byte-Länge des Strings gelesen wird.
EleLa - Elektronik Lagerverwaltung - www.elela.de

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von Socke »

MmVisual hat geschrieben:Wenn der Typ String im Speicher nicht nur die Länge sondern auch die Codierung beinhaltet, dann wüsste das der Compiler immer von alleine. Ich denke das ist das Hauptproblem.
Das Hauptproblem liegt wohl eher darin, dass viele Programmierer die Zeichenketten ganz anders als vorgesehen einsetzen. Ein Beispiel wäre das alte Lazarus-Ressourcen-System, welches Formular-Daten (ggf. Binär) in einer Zeichenkette ablegt.
Die Vorstellung, der Compiler weiß alles, weil er ja sowieso alles weiß, und könne deshalb alles machen, finde ich unrealistisch. Natürlich muss der Programmierer wissen, welche Zeichenkodierung er in seinen Quelltexten verwendet und dies muss dem Compiler auch mitgeteilt werden! In der Praxis ist dies (wie auch einige Nachfragen hier um Forum belgen sollten) häufig nicht der Fall. Compiler-Magic geht eben auch nur soweit, wie man sie selbst verstanden hat.
Wenn für den Compiler klar ist, welche Zeichenkodierung im Quelltext vorhanden ist, und diese mit der Vorstellung des Programmierers übereinstimmt, sehe ich aber noch lange keinen Bedarf an einer automatischen Konvertierung, da diese meiner Meinung nach von einem guten Software-Entwurf ablenkt -- frei nach dem Motto: ich programmiere und der Compiler wird sich schon darum sorgen, dass alles läuft.
In einem guten Entwurf (so meine Meinung) gibt es eine interne Codierung, die vom Programm bei allem verwendet, was drinnen bleibt. Jede externe Kommunikation läuft ohnehin über eine fest definierte Schnittstelle, in der auch die Zeichenkodierung definiert sein muss (außer es wird nur als Byte-Folge behandelt).
Im Fall der Compiler-Magie müssen die Schnittstellen bereits mit den entsprechenen Zeichenketten-Typen deklariert werden. SQLite bietet zum Beispiel Funktionen für UTF-8 und UTF-16 an -- diese benötigen dann entsprechende Wrapper-Funktionen, die eine automatische Konvertierung ermöglichen.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

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: Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von mschnell »

MmVisual hat geschrieben:In Delphi XE gibt es z.B. String oder AnsiString. Die Konvertierung ist auch ganz einfach:
s: String;
sa: AnsiString = "askdüööß";
s := String(sa);
Wenn ich recht informiert bin ist das bei Delphi so:

Die Codierung von ANSIString ist ANSI / locale abhängig, 1 Byte pro Zeichen. Also hier ANSI mit der für Deutschland passenden Codepage (anders als bei Lazarus, da ist ein "ANSIString" irreführenderweise meist UTF-8 codiert).

Die Codierung von "String" ist dynamisch.

Bei der Zuweisung oben wird die Codierung übernommen. s ist dann auch als ANSI mit der für Deutschland passenden Codepage abgespeichert. Und in s ist vermerkt, dass die Kodierung so ist.

Wenn man s mit einem String, der aus der VCL kommt (der ist dann UTF-16 codiert) zusammenbringt (z.B. + oder Vergleich), wird einer der Strings automatisch konvertiert. Kostet natürlich Rechenzeit.

-Michael
Zuletzt geändert von mschnell am Di 4. Okt 2011, 22:31, insgesamt 1-mal geändert.

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: Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von mschnell »

Socke hat geschrieben:Im Fall der Compiler-Magie müssen die Schnittstellen bereits mit den entsprechenden Zeichenketten-Typen deklariert werden.
Leider gab es in FPC nie verschiedene String-Typen für die verschiedenen Codierungen (z.B. locale-basiertes ANSI und UTF8). Dann hätte eine automatische "Compiler-Magic" konvertierung stattfinden können. Hat aber nie jemand realisiert.
Statt dessen kommen jetzt (wie in Delphi) eben die dynamisch codierten Strings. Auch gut ! Für den Programmierer praktischer, kann aber unerwartet Rechenzeit kosten.

-Michael

Patito
Beiträge: 203
Registriert: Di 22. Sep 2009, 13:08
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von Patito »

Ich hoffe mal, dass die Performance der Strings dabei nicht ganz vergessen wird. Ich habe letztin eine
Delphi Container Library umgeschrieben und dort Variants durch elementarere Typen ersetzt.
Das ganze war plötzlich eine Größenordnung schneller.
Wenn in CmpStr()-Funktionen unsinniger overhead drin ist könnte das schnell weh tun.

Normalerweise gehört z.B. UTF8 in einen UTF8-Typ. Wichtiger als so ein allgemeines String-Dingsbums
wäre eigentlich ein vernünftiger Satz von String-Routinen für die verschiedenen Unicode-Typen
(UTF8-Lib, UTF16-Lib, ConversionTools, ...)
Daraus kann man sich dann immer noch nach belieben einen Multi-Typ-Container basteln.

Man sollte möglichst versuchen die Fehler von Delphi nicht zu wiederholen.
Dort war das ganze wohl ein ziemlicher GAU. Ein paar Sachen die dort ziemlich übel gelaufen sind:

1) Es fehlt in Delphi eine Vernünftige Art String-Konstanten zu deklarieren. Man muß schon die
ganze Magie des Compilers reverse-engineert haben um zu verstehen was man da letztendlich bekommt.

2) Kompatibilität (!!!!!!)

3) Performance

4) Irrsinnige Namensbezeichnungen (Ansi-Funktionen, die Unicode-Funktionen sind, ...)

5) Man hat vergessen, dass ASCII wichtig ist. Alles was ASCII ist immer in UTF-16 zu speichern ist z.B. grober unfug.
Und dann gibt es noch so Sachen wie Barcodes, die manchmal ASCII + ein paar Steuerzeichen brauchen.

Displaced
Beiträge: 83
Registriert: So 12. Jul 2009, 10:08

Re: Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von Displaced »

Sorry wenn ich das mal dazwischen werfe, aber Momentan stelle ich mit den String so vor:
4 Bytes längeninfo + Zeichen (1 Byte pro Zeichen)

Oder sehe ich das falsch? Wenn nicht, ist das doch gut so... Wenn man nen String mit 2 Bytes pro Zeichen haben will, ist doch WideString die beste Wahl.
Ich finde, dass man das ruhig so lassen kann.

Und Btw
Was gibt denn Length("Das ist eine Zeichenkette") Zurück? Ich erwarte eigentlich, und bin auch der Meinung, dass es genau 25 zurück gibt.

Patito
Beiträge: 203
Registriert: Di 22. Sep 2009, 13:08
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von Patito »

Displaced hat geschrieben:Sorry wenn ich das mal dazwischen werfe, aber Momentan stelle ich mit den String so vor:
4 Bytes längeninfo + Zeichen (1 Byte pro Zeichen)

Oder sehe ich das falsch? Wenn nicht, ist das doch gut so... Wenn man nen String mit 2 Bytes pro Zeichen haben will, ist doch WideString die beste Wahl.
Ich finde, dass man das ruhig so lassen kann.

Und Btw
Was gibt denn Length("Das ist eine Zeichenkette") Zurück? Ich erwarte eigentlich, und bin auch der Meinung, dass es genau 25 zurück gibt.
Bei Unicode ist das eben nicht mehr so einfach. Unicode braucht 21 bit, daher passt in 2 Byte eben nicht alles rein.
Es gibt:
UTF32 - 32 bit pro Zeichen (schön, verbraucht aber irrsinnig Platz; Length() funktioniert einfach)
UTF16 - 16 oder 32 bit pro Zeichen (mit Length() wirds schon schwieriger)
UTF8 - 8,16,24 oder 32 bit pro Zeichen (spart am meisten Platz)

UCS-2 - 16 bit pro Zeichen, (dafür war wohl WideString gedacht, ist aber eben unvollständig)

(Korrigiert mich wenn ich da was falsch sehe)

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

Re: Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von theo »

Imho könnte man die ganze G'schicht auf UTF-8 umstellen, so wie es in Lazaraus auch gemacht ist.
Das Problem ist eigentlich nur, dass sich die FPC Entwickler nicht festlegen wollen, und so haben wir afaik z.B. immer noch keine Unicode-tauglichen File Funktionen. Wenn man sich nicht festlegen will, braucht es halt einen "Multikulti" String. Ich bräuchte ihn nicht.

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: Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von mschnell »

Displaced hat geschrieben:Sorry wenn ich das mal dazwischen werfe, aber Momentan stelle ich mit den String so vor:
4 Bytes längeninfo + Zeichen (1 Byte pro Zeichen)
Das ist allerdings völlig falsch. Erstmal gibt es ShortStrings, normale Strings, WideStrings und pchars. Die sind völlig unterschiedlich organisiert.

(nur ShortStrings sind tatsächlich (ein Byte) Längenangabe und daran anschießend ein Byte pro Zeichen.)

Du meinst vermutlich normale Strings. Die bestehen neben einem Zeiger auf den eigentlichen Inhalt aus einer Längen-Angabe und einem Referenz-Zähler.

Zwei Strings mit gleichem Inhalt können auf denselben physikalischen "Inhalt" zeigen (z.B., nach Ausführung von s1 := s2) ). Erst wenn einer verändert wird, wird der Inhalt physikalisch kopiert. ("lazy copy")

Der Referenzzähler organisiert, wann der physikalische Inhalt freigegeben werden kann.

Vor Unicode Zeiten war die Länge tatsächlich in "Zeichen" angegeben (intern und bei "Length()"). Seit Unicode ist die Länge intern und extern in "Byte". Da - je nach Codierung unterschiedlich - ein Unicode-Zeichen mehrere Bytes umfassen kann, hat Length() mit der Länge in Zeichen nun nichts mehr zu tun. Um die Länge in Zeichen zu ermitteln (oder die Position eines bestimmten Zeichens in Einheiten von Zeichen) müssen - genau wie zur Konvertierung zwischen den verschiedenen Codierungen - momentan explizit die richtigen Funktionen aufgerufen werden, weil der Compiler nicht weiß, welche Codierung in einer Variable vom Typ String (oder ANSIString) verwendet wird und die "automatische Konverierung", die man meint sie müsse geschehen, deshalb "scheinbar" falsche Resultate liefert.

-Michael
Zuletzt geändert von mschnell am Mi 5. Okt 2011, 13:13, insgesamt 2-mal geändert.

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: Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von mschnell »

theo hat geschrieben:Imho könnte man die ganze G'schicht auf UTF-8 umstellen, so wie es in Lazaraus auch gemacht ist.
Leider funktioniert dabei noch nicht einmal

MyWideString := 'Das ist eine Konstante äöü';

(Zum Testen nicht in einen String zurück-wandeln sondern wirklich Wortweise den Inhalt angucken !)

-Michael

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

Re: Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von theo »

mschnell hat geschrieben: Leider funktioniert dabei noch nicht einmal
MyWideString := 'Das ist eine Konstante äöü';
Das ist aber mit dem Multikulti String nicht automatisch behoben.
Das wäre ein Compiler Feature, dass er WideString Zuweisungen in UTF-8 Quellen automatisch umwandelt.

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von Socke »

theo hat geschrieben:Das wäre ein Compiler Feature, dass er WideString Zuweisungen in UTF-8 Quellen automatisch umwandelt.
Es gibt einen Compilerschalter, mit dem die Quelltextcodierung eingestellt werden kann. Soweit ich weiß, ist diese zwingend anzugeben, wenn der Kernbestandteil der Sprache (Schlüsselworte, Bezeichner) nicht in einem Byte abgelegt werden können (das heißt, der Quelltext ist zum Beispiel in UTF-16 abgespeichert).
Insofern kann eine Zuweisung einer konstanten Zeichenkette im selben Quelltext an Variablen unterschiedlicher Typen je nach Betrachtung das gleiche oder ein unterschiedliches Ergebnis liefern:
  • Die Zeichenkette ist in einer für den Typen vorgesehen Kodierung
    • Zuweisung in UTF-8 Quelltext an UTF-8-Variable
    • Zuweisung in UTF-16 Quelltext an UTF-16-Variable
  • Die Zeichenkette behält die Bytereihenfolge wie im Quelltext
    • Zuweisung in UTF-8 Quelltext an UTF-8-Variable ist richtig
    • Zuweisung in UTF-8-Quelltext an UTF-16-Variable ist falsch
Natürlich kann man auch in UTF-8-Quelltext einen Widestring korrekt kodieren, aber das muss man dann manuell machen (mywide: Widestring = #FF#FF; // mit entsprechendem Zeichencode)
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

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

Re: Ver(schlimm)bessertung für String-Handling (Unicode etc)

Beitrag von theo »

Socke hat geschrieben: Natürlich kann man auch in UTF-8-Quelltext einen Widestring korrekt kodieren, aber das muss man dann manuell machen (mywide: Widestring = #FF#FF; // mit entsprechendem Zeichencode)
Joh. Die Frage ist mal wieder, warum ich das wollen sollte.
Man kann immer Beispiele konstruieren, welche umständlich sind.
Hat aber irgendwie mit dem Kern des Problems nicht viel zu tun.

Antworten