Strings und Zeichenkodierung

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
Kay
Beiträge: 134
Registriert: So 14. Nov 2010, 15:17

Strings und Zeichenkodierung

Beitrag von Kay »

Hallo zusammen,

welche Strintypen für die unterschiedlichen Zeichenkodierungen zur Verfügung stehen, wurde ja bereits in diesem Forum ausführlich diskutiert.
Allerdings habe ich noch einige konkretere Fragen dazu:
1. Was genau ist "String" für ein Datentyp - ein "AnsiString" oder ein "Utf8String" oder wird das automatisch an die Verwendung angepasst?
2. Wenn "String" ein "Utf8String" sein sollte, was passiert dann, wenn ich eine Datei lese, deren Daten im ANSI-Zeichensatz kodiert sind? Wird der Datentyp dann automatisch auf "AnsiString" geändert, erfolgt eine automatische Umkodierung auf UTF-8 oder enthält meine Zeichenfolge dann "ungültige" Zeichen?
3. Gibt es eine Möglichkeit, vor der Verarbeitung einer Datei deren Zeichensatz zu bestimmen?
4. Wenn ich in der Lazarus-IDE unter Dateieinstellungen die Zeichenkoderung auf UTF-8 gestellt habe und ich weise einem Textfeld zur Entwurfszeit eine Zeichenfolge zu, ist die dann auch automatisch in UTF-8 kodiert?

Code: Alles auswählen

Edit1.Text := 'Tütensuppe';
5. Was ist mit Text, den ich zur Laufzeit in ein Textfeld eingebe?
6. Wie verhält es sich bei Dateinamen, die aus einem Open-Dialog ausgewählt werden:

Code: Alles auswählen

Edit1.Text := OpenDialog1.FileName;
Meines Wissens nach werden Dateinamen in Windows mit ANSI kodiert. Allerdings kann es natürlich auch sein, dass die TOpenDialog-Komponente eine automatische Umkodierung vornimmt. Bei der Funktion FileExists() dürfte ich dann allerdings Probleme bekommen oder?

Code: Alles auswählen

If FileExists(Edit1.Text) Then ...
7. Wenn ich den Inhalt eines Textfeldes in eine Datei schreibe, bleibt dann der Unicode-Zeichensatz erhalten oder wird der Text automatisch auf den im System verwendeten Zeichensatz geändert?
8. Worin genau besteht jetzt eigentlich der Unterschied zwischen den Funktionen "AnsiToUtf8" und "Utf8Encode"?

Viele Grüße
Kay

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: Strings und Zeichenkodierung

Beitrag von mschnell »

In Lazarus (aktuelle "Unicode-fähige" Version) sind Strings immer UTF-8 kodiert und (weil der aktuelle FPC keinen expliziten String Typ dafür anbietet) in einem String-Typ namens "ANSIString" gespeichert. In Windows macht die LCL automatisch eine Konvertierung der (normalerweise 2-Byte-Charachter = "WideString") Codierung des Betriesbssystems von/zu UTF-8. Eine korrekte automatische Konvertierung von/zu WideString (wenn Du das in Deinem Code verwenden möchtest) findet nicht (immer) statt. (Wenn Du eine String-Konstante schreibst, ist die (bei normaler Einstellung der IDE) UTF-8. Wenn Du sie einem Widestring zuordnest, wird sie automatisch falsch konvertiert.) Es gibt die entsprechenden Konvertierungs-Funktionen in der LCL, Du musst sie aber explizit aufrufen. Also Vorsicht bei Verwendung von Widestring: immer explizit konvertieren, auch bei allen Aufrufen von LCL-Funktionen und Properties.

zu 7: welche Datei-Funktionen meinst Du ? Soweit ich weiß arbeiten alle Datei-Funktionen einfach Byteweise und verändern die Information nicht.

zu 8: Wie gesagt, Lazarus speichert UTF-8 in einem Stringtpy der letztendlich der FPC-Typ "AnsiString" ist. Das führt leicht zu Verwirrung, aber FPC bietet halt keinen expliziten Typ für UTF-8 an. Daher muss Du explizit mit Funktionsaufrufen verwalten, welche Codierung ein mit 8-Bit Einheiten codierter String verwendet.

Es gibt im der FPC svn einen Alpha-Test Zweig, der dynamisch codierte Strings ("New Delphi Strings") anbietet, in denen alle Codierungsarten (jedes länderspezifische ANSI, UTF-8, UTF-16, UCS-2, UCS-4 und anders - wie "raw-byte-stream" automatisch verwaltet wird. Es ist aber noch nicht so weit, dass es von Lazarus eingesetzt werden könnte.

-Michael

Teekeks
Beiträge: 359
Registriert: Mi 27. Mai 2009, 20:54
OS, Lazarus, FPC: OpenSuse11.4 x86 (Lazarus: 0.9.30 FPC 2.4.2)
CPU-Target: x86
Wohnort: Cottbus

Re: Strings und Zeichenkodierung

Beitrag von Teekeks »

Hallo:
Das hängt vom Kompatibilitätsmodus ab:

FPC-Modus, Turbo-Pascal-Modus, ObjFPC-Modus und MAC-Modus
Strings sind ShortStrings.
Das kann mit dem Compilerschalter

Code: Alles auswählen

{$H+}
auf AnsiString umgestellt werden.

Delphi-Modus
Strings sind AnsiStrings.
Das kann mit dem Compilerschalter

Code: Alles auswählen

{$H-}
auf ShortString umgestellt werden.

Allgemein kannst du immer mit

Code: Alles auswählen

{$H+}
auf AnsiString und mit

Code: Alles auswählen

{$H-}
auf ShortString umstellen.

Edit: meins ergänzt sich ja Perfekt mit dem von mschnell

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

Re: Strings und Zeichenkodierung

Beitrag von theo »

1. Der Datentyp ist für beide gleich. Im Grunde ein Byte-Array. (Referenzzählung etc. spielt für diese Frage keine Rolle). Nur was drin steht ist anders.
2. Nicht-UTF-8 Dateien musst du explizit umwandeln.
3. Nicht 100%ig, man kann bei ANSI nicht wissen, ob z.B. Russisch (KOI8-R) oder Latin1 verwendet wurde. http://wiki.lazarus.freepascal.org/Theodp" onclick="window.open(this.href);return false;
4. Ja
5 Auch wie 4
6 UTF-8
7. Da wird nichts automatisch geändert.
8. "AnsiToUtf8" resp. "SysToUtf8" : Wandelt ANSI Systemcodierung nach UTF-8. Macht meistens nix unter Linux, weil System ist bereits UTF-8.
"Utf8Encode": Macht aus einem WideString (2Byte pro Char) ein UTF-8 String.

Kay
Beiträge: 134
Registriert: So 14. Nov 2010, 15:17

Re: Strings und Zeichenkodierung

Beitrag von Kay »

Hallo zusammen,

zunächst möchte ich mich für eure ausführlichen Antworten bedanken.

@Michael:
Ja, so ähnlich hab ich mir das schon gedacht. Allerdings war ich etwas unsicher, da es ja auch die Typen "AnsiString" und "Utf8String" in Lazarus gibt. Das ist dann etwas verwirrend.

@Teekeks:
Also ich verwende stets das von Lazarus automatisch gesetzte {$mode objfpc}{$H+}, insofern dürfte das ja kein Problem sein.

@theo:
Wenn ich das richtig verstehe, müsste ich also, wenn ich prüfen will ob der Inhalt eines Textfeldes eine vorhandene Datei bezeichnet, den Text explizit in ANSI umwandeln (unter Windows):

Code: Alles auswählen

If FileExists(Utf8ToAnsi(Edit1.Text)) Then
Andernfalls bekomme ich ein falsches Ergebnis oder?

Gut, soweit zur Theorie. Aber wie verhält es sich bei der Verwendung von String-Funktionen? Hier treten ja jetzt massive Probleme auf:

Code: Alles auswählen

var
  S: String;
begin
  S := 'Tütensuppe';
  ShowMessage(IntToStr(Length(S)));
  ShowMessage(Copy(S, 1, 4));
end;
Die Zeichenfolge enthält zwar nur 10 Zeichen, aber die Funktion Length() gibt den Wert 11 zurück. Im Prinzip ist das auch logisch, da das "ü" in UTF-8 mit 2 Bytes kodiert wird, während die anderen Zeichen mit einem Byte kodiert werden. Müsste die Funktion aber nicht eher die Anzahl der tatsächlichen Zeichen statt der Anzahl der Bytes, die zur Kodierung verwendet werden, zurückgeben? Problematisch ist dann natürlich auch die Verwendung der Funktion Copy(), da hier jetzt völlig falsche Teilzeichenfolgen kopiert werden.
Kann man das Problem irgendwie umgehen?

Viele Grüße
Kay

Teekeks
Beiträge: 359
Registriert: Mi 27. Mai 2009, 20:54
OS, Lazarus, FPC: OpenSuse11.4 x86 (Lazarus: 0.9.30 FPC 2.4.2)
CPU-Target: x86
Wohnort: Cottbus

Re: Strings und Zeichenkodierung

Beitrag von Teekeks »

Nutze UTF8Copy und UTF8Pos aus LCLProc.
Dann geht alles wieder :)

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

Re: Strings und Zeichenkodierung

Beitrag von theo »

Für Dateinamen gibt's hier noch Infos:
http://wiki.lazarus.freepascal.org/LCL_ ... _filenames" onclick="window.open(this.href);return false;

Ansonsten gibt's in Unit LCLProc noch jede Menge UTF8 Funktionen:
UTF8Pos, UTF8Copy, UTF8Delete, UTF8Insert etc.

marcov
Beiträge: 1102
Registriert: Di 5. Aug 2008, 09:37
OS, Lazarus, FPC: Windows ,Linux,FreeBSD,Dos (L trunk FPC trunk)
CPU-Target: 32/64,PPC(+64), ARM
Wohnort: Eindhoven (Niederlande)

Re: Strings und Zeichenkodierung

Beitrag von marcov »

Kay hat geschrieben: 1. Was genau ist "String" für ein Datentyp - ein "AnsiString" oder ein "Utf8String" oder wird das automatisch an die Verwendung angepasst?
Delphi (2007-) ansistring wenn {$H+} oder {$mode delphi}, sonst shortstring.

Auf FPC ebene heißt das der System Encoding. Lazarus nimmt aber an das es _immer_ utf-8 ist. User müssen selber Konversionen einfügen wo nötig
2. Wenn "String" ein "Utf8String" sein sollte, was passiert dann, wenn ich eine Datei lese, deren Daten im ANSI-Zeichensatz kodiert sind? Wird der Datentyp dann automatisch auf "AnsiString" geändert, erfolgt eine automatische Umkodierung auf UTF-8 oder enthält meine Zeichenfolge dann "ungültige" Zeichen?
Gar nichts. utf8string ist (noch) 100% dasselbe als ansistring. Der andere Namen ist nur ein Hint um Konversionen ein zu fügen.

Das aendert sich in D2009+, aber FPC implementiert das noch nicht. (es gibt etwas experimentelles, aber das ist noch immer weit von seriösen Nutzen entfernt)

Dort konvertiert ansistring<>utf8string automatisch.
3. Gibt es eine Möglichkeit, vor der Verarbeitung einer Datei deren Zeichensatz zu bestimmen?
Nein, aber die meiste lese Routinen werden das binär einlesen, also wenn man weißt das den Encoding anders als der System Encoding ist, kann man konversionen auch NACH laden einfügen.
4. Wenn ich in der Lazarus-IDE unter Dateieinstellungen die Zeichenkoderung auf UTF-8 gestellt habe und ich weise einem Textfeld zur Entwurfszeit eine Zeichenfolge zu, ist die dann auch automatisch in UTF-8 kodiert?
Ich denke nicht, ich denke dass das Encoding von sogenannte Literals setzt (via den -Fc Parameter von FPC).

Code: Alles auswählen

Edit1.Text := 'Tütensuppe';
In den übrigen Beispiel ist 'Tütensuppe' ein Literal.
5. Was ist mit Text, den ich zur Laufzeit in ein Textfeld eingebe?
Das geht direkt nach Lazarus/LCL, also UTF8
6. Wie verhält es sich bei Dateinamen, die aus einem Open-Dialog ausgewählt werden:

Code: Alles auswählen

Edit1.Text := OpenDialog1.FileName;
OpenDialog1 ist ein LCL component, also UTF8.
Meines Wissens nach werden Dateinamen in Windows mit ANSI kodiert. Allerdings kann es natürlich auch sein, dass die TOpenDialog-Komponente eine automatische Umkodierung vornimmt. Bei der Funktion FileExists() dürfte ich dann allerdings Probleme bekommen oder?
Nein:

- Windows NT (NT4,2000,XP,Vista) hat zwei Satze Fukctionen fuer fast alles. -A funktionen nehmen ASCII in den System Encoding, und -W funktionen nehmen UTF16 unicode.
- Win9x hat normal nur -A Versionen
- Lazarus nutzt meistens -W Funktionen, aber intern konvertiert UTF8 nach UTF16

Es gibt nicht nur ansi und wide (resp. system ansi und utf8), aber auch noch OEMString. Das ist die Dos kompatibeler Encoding, und wird für Konsole (Dos) Windows genutzt.

Code: Alles auswählen

If FileExists(Edit1.Text) Then ...
Edit1.text ist Lazarus LCL, also UTF8, FileExists ist FPC, also System Encoding.
7. Wenn ich den Inhalt eines Textfeldes in eine Datei schreibe, bleibt dann der Unicode-Zeichensatz erhalten oder wird der Text automatisch auf den im System verwendeten Zeichensatz geändert?
Via LCL schreibe Weisen: hoffentlich UTF-8. Via FPC routinen: System Encoding.
8. Worin genau besteht jetzt eigentlich der Unterschied zwischen den Funktionen "AnsiToUtf8" und "Utf8Encode"?
Ich glaube den erste ist Windows/Delphi, den zweiten ist FPC und portabel.

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: Strings und Zeichenkodierung

Beitrag von Socke »

marcov hat geschrieben:- Windows NT (NT4,2000,XP,Vista) hat zwei Satze Fukctionen fuer fast alles. -A funktionen nehmen ASCII in den System Encoding, und -W funktionen nehmen UTF16 unicode.
- Win9x hat normal nur -A Versionen
- Lazarus nutzt meistens -W Funktionen, aber intern konvertiert UTF8 nach UTF16

Es gibt nicht nur ansi und wide (resp. system ansi und utf8), aber auch noch OEMString. Das ist die Dos kompatibeler Encoding, und wird für Konsole (Dos) Windows genutzt.
Könnte mir bitte jemand die Unterschiede zwischen UCS-2 und UTF-16 sowie zwischen UCS-4 und UTF-32 erklären. Die dazugehörigen Wikipedia-Artikel sind widersprüchlich: es heißt, UTF-32 sei eine Untermenge von UCS-4, aber UCS-4 entspräche UTF-32.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Kay
Beiträge: 134
Registriert: So 14. Nov 2010, 15:17

Re: Strings und Zeichenkodierung

Beitrag von Kay »

Ich glaube die Unterschiede der Kodierungsformate liegen lediglich darin, ob die Anzahl der zur Kodierung verwendeten Kodeeinheiten fest oder variabel ist und wieviele Bits die Kodeeinheit jeweils umfasst.

Viele Grüße
Kay

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

Re: Strings und Zeichenkodierung

Beitrag von theo »

Soviel ich weiss, ist UCS-4 und UTF-32 in Prinzip dasselbe.
UCS-2 und UTF-16 sind innerhalb des BMP auch identisch ($0000 to $FFFF).
UTF-16 kann aber auch die darüberliegenden Code-Points mit Hilfe von Surrogaten darstellen. UCS-2 nicht, dafür braucht man da keine "Escape Sequenzen" berücksichtigen.

marcov
Beiträge: 1102
Registriert: Di 5. Aug 2008, 09:37
OS, Lazarus, FPC: Windows ,Linux,FreeBSD,Dos (L trunk FPC trunk)
CPU-Target: 32/64,PPC(+64), ARM
Wohnort: Eindhoven (Niederlande)

Re: Strings und Zeichenkodierung

Beitrag von marcov »

theo hat geschrieben:Soviel ich weiss, ist UCS-4 und UTF-32 in Prinzip dasselbe.
UCS-2 und UTF-16 sind innerhalb des BMP auch identisch ($0000 to $FFFF).
Das ist nicht korrekt. UTF-16 hat glaube ich nur etwa 40k(*) Zeichen aus der BMP, weil ganze Menge Kodepunkten fuer Enkodung von höhere Kodepunkten benoetigt werden. Es ist zu vergleichen mit das UTF-8 nur die untere 7 bits binär (0..127) Kompatibel mit ASCII ist, das höchsten Bit ist benoetigt fuer Enkoding Zwecken.

Wenn ich auf Wikipedia nachschaue lese ich da das UTF-16 8k reserviert fuer Surrogate escape kodes, also so komme ich auf 64k-8k=56k. Es koentte aber sein das die escape kodes in der BMP nicht definiert sein, dann ist UTF16 tatsaechlich aehnlich zu UCS2 (aber nur maximal 56k nutzbare Zeichnen, mit einen Bereich 0..D7FF, E000.FFFF)
UTF-16 kann aber auch die darüberliegenden Code-Points mit Hilfe von Surrogaten darstellen. UCS-2 nicht, dafür braucht man da keine "Escape Sequenzen" berücksichtigen.
UCS2 ist tot, und hat seit 4.0 glaube ich keinen formeller Status innerhalb Unicode. (es ist glaube ich ein Encodung aus einer Unicode Draft, 1.1)

Windows2000 ist der letzter Windows der UCS2 ist. Windows XP (fast zehn jahre alt!) ist UTF-16, aber sein Font-system kann in nicht-Ost Asiatische Versionen nur 65535 Codepoints verwalten. Erst mit Vista verschwindet der Unterschied zwischen Ost Asiatische Versionen und normale Versionen und ist alles UTF16.
Zuletzt geändert von marcov am Mo 22. Nov 2010, 00:19, insgesamt 1-mal geändert.

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

Re: Strings und Zeichenkodierung

Beitrag von theo »

marcov hat geschrieben: Das ist nicht korrekt.
Ich glaube, das ist schon korrekt.
The encoded values use the range of numbers from 0xD800 to 0xDFFF. These results overlap with an unassigned portion of the UCS-2 encoded values,

marcov
Beiträge: 1102
Registriert: Di 5. Aug 2008, 09:37
OS, Lazarus, FPC: Windows ,Linux,FreeBSD,Dos (L trunk FPC trunk)
CPU-Target: 32/64,PPC(+64), ARM
Wohnort: Eindhoven (Niederlande)

Re: Strings und Zeichenkodierung

Beitrag von marcov »

theo hat geschrieben:
marcov hat geschrieben: Das ist nicht korrekt.
Ich glaube, das ist schon korrekt.
Mein Edit hat gekreuzt mit deiner Antwort. Habe das auch herausgefunden. Aber dies geltet nur für Decoder die (utf16,UCS2) nach etwas anders Übersetzen, nicht für andere Unicode nutzen. (die zb UTF16 schreiben konnten)

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: Strings und Zeichenkodierung

Beitrag von mse »

@Kay
Falls noch nicht bekannt: MSEide+MSEgui arbeitet mit 16 Bit UnicodeString statt utf-8 codierter AnsiString. Damit lassen sich die Programme auch mit -Fcutf8 kompilieren und es gibt keine Tütensuppen-Probleme.

Martin

Antworten