String-Typen in Pascal

Pascal hat einen schönen String – etwas „Stringtheorie“ für Programmierer

Übersicht: folgende Datentypen (anklickbar) werden von modernen Pascal-Compilern angeboten, um Zeichenketten zu speichern. Auf dieser Seite werden außerdem folgende Themen behandelt: PChar, const-Parameter sowie Speicher und Referenz­zählung.

Array of Charhistorisch
ShortStringUTF-8 oder ANSI, max. 255 Bytes, ohne Referenzzählung
Stringsystemabhängig AnsiString oder UnicodeString
AnsiStringUTF-8 oder ANSI
UTF8StringUTF-8-typisierter AnsiString
RawByteStringuntypisierter AnsiString, übernimmt Codepage der Quelle
WideStringUTF-16, ohne Referenzzählung
UnicodeStringUTF-16
UCS4StringUTF-32, Array of UCS4Char

Einleitung

Mit den Typen AnsiString und UnicodeString haben Delphi und Free­Pascal/Laza­rus sehr mäch­tige, prak­ti­sche und schnelle String-Typen be­kom­men. Beide Typen füh­ren eine Längen­angabe mit sich (ein „counted string“ im Gegen­satz zu einem Null-termi­nier­ten String), können dyna­misch so viel Speicher reser­vieren wie die System­architek­tur und das Betriebs­sysstem her­geben, wissen mit welcher Zeichen­kodie­rung ihr Inhalt kodiert wurde, und haben eine Referenz­zählung, um unnöti­ges Kopie­ren des Inhalts zu ver­mei­den.

Das ist alles sehr komfor­tabel und lässt kaum Wünsche offen – außer viel­leicht, dass die Sprache des Inhalts, z.B. de, en, fr, etc., nicht in den Meta­daten hinter­legt wer­den kann. Und in einem besonde­ren Fall kann die Referenz­zählung von Nach­teil sein, näm­lich wenn ein String-Inhalt in mehre­ren Threads (Multi-Threading) ge­nutzt wer­den soll, muss sich der Program­mie­rer vorab darum küm­mern, jedem Thread eine eigene String-Kopie zu über­geben.

Aus heutiger Sicht ist die Namens­gebung einiger String­typen etwas unglück­lich. Der Unicode­String müsste eigentlich UTF16­String heißen (analog zum UTF8­String), denn poten­tiell alle String­typen kön­nen Unicode-Zeichen ent­hal­ten, weil UTF-8 und UTF-32 ebenso zum Unicode-System ge­hören wie UTF-16. Und der UCS4­String sollte besser UTF32­String heißen, denn UCS4 wurde vom UTF-32-Standard ab­ge­löst (funk­tionell iden­tisch).

String

Der Typ String ist kein eindeu­ti­ger Typ, sondern hängt von der Umge­bung und den Compiler-Einstel­lun­gen ab. In Free­Pascal und Laza­rus ent­hält eine neue Unit standard­mäßig den Compiler-Schalter {$H+}, was be­deu­tet, dass der Typ String ein langer String sein soll. Ein langer String ist in Free­Pascal ein AnsiString (UTF-8). Dahinge­gen ist ein langer String in Delphi ab Version 2009 ein UnicodeString (UTF-16).

Ist dieser Compiler-Schalter in einer Unit nicht vor­han­den oder als {$H-} an­ge­geben, ist der Typ String ein ShortString mit 255 fest reser­vier­ten Bytes für den Inhalt.

Heap-Speicher und Referenzzählung

Die modernen String-Typen in Delphi und Free­Pascal, d.h. Ansi­String und Unicode­String, sind referenz­gezählt. Ausge­nom­men sind die Typen Short­String, Wide­String und UCS4­String.
Die Referenzierung eines Inhalts be­deu­tet, dass eine String-Varia­ble intern nur ein Zeiger ist, der auf den Inhalt zeigt, welcher evtl. von mehre­ren String-Varia­blen ge­mein­sam ge­nutzt wird.

Wird einem AnsiString ein konstan­ter Inhalt zu­ge­wie­sen, z.B.: s := ‚Hallo‘;, er­hält die Varia­ble s nur einen Zeiger auf die Kons­tante, die im read-only Code-Segment des Pro­gramms ge­spei­chert ist. Die Funktion String­RefCount(s) gibt hier­bei -1 als Referenz­zähler zu­rück, wo­ran man eine Konstan­ten­zuwei­sung er­ken­nen kann.

Heap-allokiert: Wird der String-Inhalt zur Lauf­zeit er­zeugt, z.B. durch eine Ver­ket­tung wie s := s + ‚ Welt!‘;, wird da­für Spei­cher­platz auf dem Heap (↗dynami­scher Speicher) reser­viert, wo der neue Inhalt als voll­stän­dige Zei­chen­kette „Hallo Welt!“ ab­ge­legt wird. Solange die neue Zeichen­kette erst einer einzi­gen Varia­blen zuge­wie­sen ist, lie­fert String­RefCount(s) den Wert 1 als Referenz­zähler.

Die AnsiString-Variable selbst ist immer nur ein Zeiger auf den Inhalt. Der Zeiger ist 4 Byte (32 Bit) bzw. 8 Byte (64 Bit) groß und wird im Gültig­keits­bereich der Varia­ble ge­spei­chert, d.h. bei einer loka­len Varia­blen auf dem Stack, oder auch z.B. in einem Record oder Objekt.

Die Metadaten werden zusam­men mit dem Inhalt im Speicher ab­ge­legt. Die fol­gen­de Tabelle zeigt die Speicher­bele­gung, wie sie bei AnsiString-Inhal­ten ver­wen­det wird. Die String-Variable als Pointer zeigt direkt auf den Inhalt, d.h. für den Zu­griff auf die Meta­daten wird intern ent­sprechend vor der Pointer-Adresse ge­lesen. Auch die Typ-Umwand­lung (Type Cast) mittels PChar(s) liefert be­kannter­maßen den Zeiger direkt auf den Inhalt (auf das erste Zeichen).

CodePageElementSizeReferenceCountLengthInhaltTerminator
2 Bytes2 Bytes4 oder 8 Bytes4 oder 8 BytesCharsChar #0


Der String-Inhalt kann in einer 32-Bit-Anwen­dung theo­re­tisch bis zu 2 GB groß sein. In einer 64-Bit-Anwen­dung könnte ein String theo­re­tisch sogar bis zu 9 Exa­byte groß sein – das wären 9 Milliar­den GB.

Wird einer String-Variablen ein String gleichen Typs zu­ge­wie­sen, wird der Inhalt nicht ko­piert, son­dern nur ein Zeiger auf den be­ste­hen­den Inhalt ge­setzt und der Refe­renz­zähler für die­sen Inhalt er­höht. Das geht schnel­ler als den Inhalt zu kopie­ren, insbe­son­dere wenn der String 9 Exa­byte groß ist. 😉

Der „Copy on Write“-Mechanis­mus kopiert den Inhalt auto­ma­tisch erst dann, wenn eine Inhalts­ände­rung es erfor­der­lich macht. Wenn die String-Variable einen ande­ren Inhalt zu­ge­wie­sen be­kommt, wird der Referenz­zähler des bishe­ri­gen Inhalts wieder ver­ringert. Wenn dabei der Zähler auf 0 fällt, bedeutet es, dass der alte Inhalt von keiner Varia­blen mehr ver­wen­det (nicht mehr refe­ren­ziert) wird und des­halb der bisher be­legte Spei­cher frei­gege­ben wird.

Ein Inhalt kann bis zu 2 Milliarden mal referen­ziert werden (32 Bit, signed?), auf 64-Bit-Systemen sogar 9 Tril­lio­nen-fach referen­ziert wer­den (laut Wiki: „On 64-bit targets, fields RefCount/Length consume 8 bytes each, not 4.“).

const-Parameter

procedure MyProcedure(const s: String);

In einem Funktions­kopf sollte eine String-Über­gabe mög­lichst als konstan­ter Para­meter mit „const“ dekla­riert wer­den, sofern inner­halb der Prozedur keine Ände­rung am String-Inhalt nötig ist. Der Vor­teil ist eine höhere Ausfüh­rungs­geschwin­dig­keit. Dies gilt auch bei ande­ren struk­tu­rier­ten Typen wie Record oder Array.

Wird der Übergabeparameter ohne „const“ dekla­riert, wird der Inhalt i.d.R. für die lokale Varia­ble ko­piert. Das dient dem Zweck, dass Ände­run­gen inner­halb der Proze­dur blei­ben und sich der Inhalt für den Aufru­fer nicht ändert, welcher wahr­schein­lich mit seinen unver­änder­ten Daten weiter­arbei­ten möchte. Eine lokale Kopie ist je­doch unnötig, wenn Inhalte nur ge­lesen und nicht ver­ändert werden. Dies zeigt die const-Dekla­ra­tion dem Compiler an.

Allerdings ist die const-Dekla­ra­tion bei moder­nen String­typen mit Referenz­zähler (Ansi­String und Unicode­String) weniger wichtig als bei frühe­ren Typen. Ohne „const“ wird z.B. ein ShortString komplett ko­piert. Aber bei einem referenz­gezähl­ten String wird ohne „const“ einfach nur der Zähler er­höht. Trotz­dem hat auch hier ein Konstant­parame­ter einen Vor­teil, denn dann wird sogar auf das Hoch- und Runter­zählen des Referenz­zählers ver­zich­tet, was immerhin noch einen klei­nen Geschwin­dig­keits­vorteil bringt.

Array[1..n] of Char

Rückblick: In den ersten Pascal-Versio­nen in den 1970ern gab es noch keinen String-Typ. Statt­des­sen musste man für eine Zeichen­kette ein stati­sches Array selber defi­nie­ren, mit einer festen Länge je nach Bedarf.

In Pascal wurde das Char-Array tradi­tio­nell mit Index 1 be­gin­nend defi­niert, weil der ↗Pascal-Erfinder die Zähl­weise ab 1 als für Men­schen natür­li­cher empfand. Auch heute noch hat sich der Start­index 1 bei allen neueren String­typen in Pascal er­hal­ten – mit Aus­nahme des UCS4String, der bloß ein Array of UCS4Char mit Start­index 0 ist.

Wenn der Inhalt nicht die volle Länge des Arrays belegt, muss man die tatsäch­lich be­legte Länge irgend­wo geson­dert spei­chern. Das war wirk­lich sehr unprak­tisch. Oder man setzt das Char hinter dem Inhalt auf #0 – als Null-terminier­ter String wie in der Pro­grammier­sprache C.

PChar

PChar ist kein Stringtyp, sondern ledig­lich ein Pointer (Zeiger) auf ein Zeichen. PChar wird haupt­säch­lich dazu ver­wendet, einen Zeiger an eine Funktion in einer API oder einer sons­ti­gen Funktions­bibliothek (Library) zu über­geben, z.B. für Funktio­nen in einer .dll (z.B. der Windows-API) oder einer .so-Datei.

Der Typ PChar reserviert keinen eigenen Speicher­platz für den Inhalt, d.h. bei der Verwen­dung von PChar muss man sicher­stel­len, dass der Speicher ander­weitig reser­viert ist und unver­ändert reser­viert bleibt – nämlich solange wie der PChar ver­wen­det wer­den könnte, um auf den Inhalt zu­zu­greifen.

PChar ist kein eindeu­ti­ger Typ, sondern kann umgebungs­abhängig ein PAnsiChar oder PWideChar sein – entspre­chend dem Compiler-Schalter, ob ein String ein Ansi­String oder Unicode­String ist.

Wendet man PChar(s) als Typ-Umwand­lung (Type Cast) an, er­hält man einen Zeiger auf das erste Zeichen im String bzw. NIL bei einem leeren String. Man muss stets be­achten, dass nur die String-Varia­ble für die Reser­vie­rung des Speichers zu­ständig ist, d.h. so­lange man den PChar als Zeiger ver­wen­den oder weiter­geben möchte, muss die String-Varia­ble unver­ändert bleiben.

Ein PChar sollte auf einen Null-terminier­ten Inhalt zei­gen, weil ein PChar keine Meta-Informa­tio­nen mit­führt und somit die Länge des Inhalts nicht kennt. Glücklicher­weise sind die Typen AnsiString und UnicodeString auto­ma­tisch Null-termi­niert, so dass man sich nicht darum zu küm­mern braucht. Wenn man keinen Null-termi­nier­ten Inhalt hat, wie z.B. in einem ShortString, muss man die Längen-Angabe ge­sondert spei­chern und weiter­geben.

ShortString
String[n]

Ein ShortString war der erste String-Typ, der in Turbo Pascal ein­ge­führt wurde. Im Prinzip funktio­niert ein Short­String ähnlich wie ein Array of Char, mit dem Unter­schied, dass das erste Element s[0] die Länge ent­hält, die vom Inhalt tatsäch­lich be­legt ist. Weil die Längen­angabe nur ein Char (1 Byte) ist, kann die Länge des Inhalts maximal 255 Bytes be­tragen (Char-Werte­bereich #0..#255). Das erste Zeichen be­findet sich im Element s[1], also weiter­hin Pascal-typisch ein 1-basier­ter Index für den Inhalt.

Die zu reservierende Kapazität des kurzen Strings muss vorab fest­ge­legt werden, z.B. als var s: String[40]; für maxi­mal 40 Bytes Inhalt. Hinzu kommt das Längen-Char, so dass der Typ String[40] immer 41 Bytes be­legt, auch wenn der String leer ist. Verwen­det man ShortString als Typ, ent­spricht es String[255], also dem maxi­malen ShortString für bis zu 255 Zei­chen und mit fes­tem Speicher­platz­bedarf von 256 Bytes.

Der Speicherort des ShortString richtet sich nach dem Gültig­keits­bereich, in dem die Varia­ble dekla­riert wird, d.h. als globale Varia­ble wird der gesamte Short­String im Data-Segment ab­ge­legt, als lokale Varia­ble auf dem Stack, als Record-Feld im Record, und als Klassen-Eigen­schaft im er­zeug­ten Objekt im Heap.

Die Zeichenkodierung des Inhalts richtet sich nach dem System bzw. erhält einen zuge­wiese­nen Inhalt in des­sen vorlie­gen­der Kodie­rung. In Free­Pascal und Laza­rus ge­langen heut­zutage meis­tens UTF-8-kodierte Zei­chen in den ShortString. In Delphi hin­gegen ist der Inhalt ANSI-kodiert, d.h. in West­europa typischer­weise mit der Code­page Windows-1252.

Ein ShortString ist nicht Null-termi­niert, d.h. wenn man ihn im Zusammen­hang mit PChar nutzen möchte, müsste man dem Inhalt manuell ein #0 an­hängen oder die Länge des ShortStrings ge­sondert weiter­geben (falls möglich).

Bei Verwendung eines kurzen Strings mit nur wenigen Zeichen, kann String[n] einen Vor­teil bezüg­lich Speicher­bedarf und Geschwin­dig­keit gegen­über referen­zier­ten Strings haben. Z.B. ist für ein Sprach­kürzel wie „de“ oder ein Landes­kürzel wie „CH“ ein String[3] mit gefäl­li­gen 4 Byte Speicher­bedarf sehr kompakt und schneller kopiert als eine Referenz hoch- und runter­gezählt ist. Das Gleiche gilt für Codes wie „de-AT“ oder kurze Wörter, die in String[7] passen. Hierbei sind die fest be­legten 8 Byte Speicher nicht länger als der Pointer eines Ansi­Strings in einem 64-Bit-System. Jedoch wird für die Ver­wen­dung in String-Funktio­nen eine Umwand­lung in einen Ansi­String oder Unicode­String er­forder­lich, was jeden Vor­teil ins Gegen­teil ver­kehrt.

AnsiString

Ein AnsiString enthält 1-Byte-Zeichen vom Typ AnsiChar. Der Typ AnsiString wurde 1996 von Delphi 2 ein­ge­führt, dem ersten 32-Bit-Delphi. Vorher musste man sich mit ShortString be­gnügen. Bezüg­lich der Speicher­verwal­tung des Ansi­Strings siehe: Heap und Referenz­zählung.

Zeichenkodierung (Character Encoding): In Delphi folgt die 1-Byte-Kodierung der Code­page des Betriebs­systems, z.B. Codepage „Windows-1252“ in West­europa. In Lazarus wechsel­te man 2016 auf UTF-8 als Standard-Zeichen­kodie­rung für Ansi­String. In Kon­solen-Anwendun­gen wird durch den Compiler-Schal­ter {$codepage utf8} eben­falls UTF-8 zur Standard­kodierung für Ansi­String.

In UTF-8 belegt ein Zeichen zwischen 1 Byte und 4 Bytes. Der Vorteil ist, dass sich darin jedes Alphabet und jedes Schrift­zeichen ab­bilden lässt. Hingegen ver­wen­den die herkömm­li­chen Codepage-Kodierun­gen nur 1 Byte pro Zei­chen. Dadurch ist der Zu­griff auf ein einzel­nes Zeichen sehr ein­fach, aber jede Code­page ist auf nur ein Alphabet be­schränkt.

AnsiString(Codepage)

Typisierung mittels Codepage-Nummer: Es ist möglich, in einem AnsiString eine spezi­fi­sche Zeichen­kodie­rung zu ver­wen­den, indem man den Ansi­String durch die Angabe einer ge­wünsch­ten Code­page typi­siert.

var
 s_cp1251: AnsiString(1251);    // nur kyrillisch
 s_cp1252: AnsiString(1252);    // nur Latin-1 (westeuropäisch)
 s_cp1253: AnsiString(1253);    // nur griechisch 

 s_utf8  : AnsiString(CP_UTF8); // = UTF8String
 s_raw   : AnsiString(CP_NONE); // = RawByteString

begin
 s_utf8 := 'Γειά σου, Κόσμε!';  // griechisch: "Hallo Welt!" 

 s_cp1253 := s_utf8;    // passt:  cp1253 ist für griechische Buchstaben

 s_raw := s_cp1253;     // passt:  ein RawByteString übernimmt jede Codepage

 s_cp1252 := s_cp1253;  // falsch: cp1252 kennt keine griechischen Buchstaben
end;

Wird einer AnsiString-Variablen eine andere Ansi­String-Variable zu­ge­wie­sen, wird bedarfs­weise eine Neu­kodie­rung auf die Ziel-Code­page vor­ge­nom­men. Das o.g. Bei­spiel demons­triert solche Spezial­fälle. Dagegen ist im Normal­fall keine Neu­kodie­rung er­forder­lich, weil in den meis­ten Program­men alles in der­selben Standard-Code­page ver­wen­det wird.

Wird einem UTF8String etwas zu­ge­wie­sen, funktio­niert das immer, weil UTF-8 sämt­li­che Unicode-Zeichen kodie­ren kann (in 1 bis 4 Bytes über­setzt). Wird jedoch in dem Bei­spiel dem s_cp1252 (nur lateinisch) ein s_cp1253 (griechisch) zuge­wie­sen, finden sich für die griechi­schen Buch­staben keine Ent­sprechun­gen in der west­europä­ischen Code­page, weshalb solche Zeichen für s_cp1252 ersatz­weise in „?“ über­setzt wer­den. Somit kann man damit nicht mehr viel an­fangen.

Vorsicht ist bei der Zuweisung einer Konstan­ten an einen typi­sier­ten String ge­boten: Es wird immer der konstante, UTF-8-kodierte Inhalt zu­gewie­sen, auch wenn der Ziel-String auf eine andere Code­page typi­siert ist und eigentlich nach einer Neu­kodie­rung ver­langt. Meiner Meinung nach ist das ein Compiler-Fehler, denn beim Aus­lesen der Varia­blen wird der kons­tante UTF-8-Inhalt durch die spezielle Codepage-Interpre­ta­tion unbrauch­bar.

Auf ein anderes Problem bin ich bei TStringList.Text gestoßen: Fügt man einer StringList Strings hinzu, die nicht der Standard-Code­page des Systems ent­sprechen, wird dies bei der Ver­arbei­tung durch .Text nicht berück­sich­tigt, d.h. man er­hält einen unbrauch­baren Gesamt-String, weil seine Standard-Code­page nicht zum anders kodier­ten Inhalt passt.

RawByteString

Ein RawByteString ist ein spezieller AnsiString, dem keine übliche Sprach-Codepage zu­ge­wie­sen ist. Wird einem RawByteString ein Ansi­String zu­ge­wie­sen, über­nimmt er ein­fach des­sen Code­page und es findet garan­tiert keine Umkodie­rung des Inhalts statt.

function ToWesternEuropean(const s: RawByteString): RawByteString;
begin
  Result := s;  // hier nur Referenzierung
  if StringCodePage(Result) <> 1252 then
    SetCodePage(Result, 1252);  // De-referenzierung und Umkodierung
end;

Von diesem Funktionskopf wird jede belie­bige Code­page akzep­tiert, ohne dass eine auto­ma­ti­sche Umkodie­rung z.B. in UTF-8 statt­findet.
Innerhalb der Funktion: Wenn der Eingabe-String s bereits in der Code­page 1252 (west­euro­pä­isch) vor­liegt, findet keiner­lei Umkodie­rung statt und es wird die gleiche String-Referenz wieder aus­ge­geben, quasi ohne Zeit­verlust.

UTF8String

Ein UTF8String ist ein typisier­ter AnsiString(CP_UTF8), d.h. er ent­hält garan­tiert einen UTF-8-kodier­ten Inhalt. Dieser Typ ist nur für Delphi rele­vant, wo ein AnsiString nicht standard­mäßig in UTF-8, sondern in der ANSI-Code­page des Sys­tems kodiert ist.

In Lazarus ist UTF-8 seit 2016 die Standard-Kodie­rung für Ansi­String, der zu­gleich dem Typ String ent­spricht. Somit kann man in Laza­rus i.d.R. ein­fach den Typ String ver­wen­den und be­nötigt UTF8String nicht.

Wenn jedoch eine in FreePascal program­mierte Unit Delphi-kompati­bel sein soll und aus be­stimm­ten Gründen auf UTF-8 an­ge­wie­sen ist, sollte man expli­zit UTF8String an­geben, damit es von beiden Compilern gleich be­handelt wird.

WideString

WideString ist ein UTF-16-String, der mit Delphi 3 (1997) speziell für Windows ein­ge­führt wurde. WideString ist die Um­set­zung des Windows BSTR-Typ (Basic String), der als nativer String-Typ für die COM-Schnitt­stelle (OLE-Automa­tion) ver­wen­det wird.

Unter Windows wird der WideString-Inhalt nicht auf dem eige­nen Heap ab­gelegt, sondern über die Win­dows-API mit­tels SysAllocString() und SysFreeString() ver­wal­tet. WideString hat des­halb keine Referenz­zählung und zudem ist die indirekte Speicher­verwal­tung über die Win­dows-API etwas lang­samer.

Da es in Linux keine COM-Schnitt­stelle gibt, wird der String-Inhalt in Free­Pascal unter Linux im eige­nen Heap ab­ge­legt. Wer also nicht auf das Win­dows COM-Inter­face zu­greift, aber einen UTF-16-String nutzen möchte, sollte lieber den Typ UnicodeString ver­wenden.

UnicodeString

Der UnicodeString ist ausschließ­lich für die UTF-16-Kodie­rung (WideChar-Elemente) und wurde mit Delphi 2009 ein­ge­führt. Die Be­zeich­nung „Unicode“String ist unglück­lich ge­wählt, da UTF-8 und UTF-32 gleicher­maßen zum Unicode-System ge­hören, aber bei diesem String­typ speziell nur UTF-16 ge­meint ist.

Für die Speicherung gilt: Heap und Referenz­zählung wie bei AnsiString. Im Gegen­satz zu AnsiString kann der UnicodeString keine unter­schied­li­chen Kodie­run­gen ent­hal­ten, sondern be­inhal­tet immer UTF-16 (Nach­folger der UCS2-Norm).

In Delphi ist UTF-16 nütz­lich, um die Win­dows-API zu be­die­nen. Deshalb ist der Typ String in Delphi seit 2009 mit UnicodeString gleich­gesetzt. In Free­Pascal/Lazarus, das auch für Linux-Program­mie­rung ge­nutzt wird, hat man sich hin­gegen für UTF-8 ent­schie­den und ist des­halb bei AnsiString ge­blieben. Deshalb spielt der UnicodeString in Laza­rus kaum eine Rolle (haupt­säch­lich zur Delphi-Kompati­bi­lität).

Leider hat UTF-16 ein ähnliches Problem wie UTF-8, näm­lich dass ein Zeichen aus mehre­ren Elemen­ten be­stehen kann. Somit bie­tet der Unicode­String beim Zugriff auf ganze Zei­chen kei­nen Vor­teil gegen­über einem UTF8String.

UCS4String

Der UCS4String ist lediglich ein Array of UCS4Char (je 32 Bit = 4 Byte) und ist dafür ge­dacht, eine UTF-32-Zeichen­kette auf­zu­nehmen. UTF-32 hat den unschlag­baren Vor­teil, dass jedes Element ein ganzes Zeichen ist.

Doch leider wird der Typ nicht gut unter­stützt. Es gibt nur wenige Funktio­nen für den UCS4String. Das letzte Element soll eine Null ent­hal­ten, um kompati­bel mit einem Null-termi­nier­ten String zu sein. Leider muss man sich darum komplett selbst küm­mern. Die String-Länge er­gibt sich aus Length(Array) ‑ 1 und kann ohne Inhalt 0 oder ‑1 sein.

Somit ist UCS4String leider eine ähn­liche Bastelei wie das histo­ri­sche Array of Char, das nie­mand mehr ver­wen­den möchte. Für UCS4String ist es eigent­lich schade, denn 4 Byte pro Zeichen sind heut­zu­tage kein Problem mehr. Würde es eine gute Unter­stüt­zung für UCS4String geben – ähnlich wie für Ansi­String – könnte es eigent­lich ein moder­ner, prakti­scher Typ sein. Vermut­lich ist der Bedarf aber durch die anderen String-Typen aus­rei­chend ge­deckt. Außerdem ist die be­stehende Defini­tion als Array of UCS4Char be­züg­lich Kompati­bi­li­tät leider eine Sack­gasse.

Fazit

Der Möglichkeiten gibt es viele, aber meis­tens ver­wen­det man doch nur den Typ String, weil er kaum Wünsche offen lässt und als Standard-String­typ des Systems von allen String-Funktio­nen unter­stützt wird.

Falls man beim Programmieren doch auf Probleme stößt, hat es häufig etwas mit einer Typ-Umwand­lung zu PChar zu tun, oder dass man bei UTF-8 nicht berück­sich­tigt hat, dass ein Zeichen bis zu 4 Bytes be­legen kann, ebenso bei UTF-16. Codepage-Konver­tie­rungem werden meis­tens nur für Spezial­fälle be­nötigt, z.B. für ANSI-Text­dateien oder für Daten­banken.