Unicode in RAD Studio
Inhaltsverzeichnis
- 1 Vorhandene String-Typen
- 2 Neuer String-Typ: UnicodeString
- 3 Compilerbedingungen
- 4 Überblick über die Änderungen
- 5 Überblick über Unverändertes
- 6 Von der Zeichengröße unabhängige Quelltextkonstrukte
- 7 Von der Zeichengröße abhängige Quelltextkonstrukte
- 8 Set of Char-Konstrukte
- 9 Achtung bei diesen Konstrukten
- 10 Laufzeitbibliothek
- 11 Komponenten und Klassen
- 12 Byte Order Mark
- 13 Schritte zum Aktivieren von Unicode in Anwendungen
- 14 Neue Warnungen des Delphi-Compilers
- 15 Empfehlungen
- 16 Siehe auch
Nach oben zu Einführung in RAD Studio
In RAD Studio werden Unicode-basierte Strings verwendet: der Typ string
ist nun ein Unicode-String (System.UnicodeString) anstatt eines ANSI-Strings. Dieses Thema beschreibt die korrekte Behandlung von Strings.
Mit den Typen AnsiString und WideString werden ANSI- bzw. Wide-Strings verwendet.
RAD Studio ist vollständig Unicode-kompatibel, und für diejenigen Teile Ihres Quelltextes, die die String-Behandlung betreffen, sind einige Änderungen möglicherweise erforderlich. Es wurde aber versucht, diese Änderungen auf ein Minimum zu beschränken. Es wurden zwar neue Datentypen eingeführt, aber vorhandene Datentypen wurden beibehalten und arbeiten genauso wie bisher. Ausgehend von dem mit der Unicode-Konvertierung im eigenen Haus gemachten Erfahrungen, sollten vorhandene Anwendungen ziemlich reibungslos migriert werden können.
Weitere Ressourcen:
- Delphi Unicode Migration for Mere Mortals: Stories and Advice from the Front Lines von Cary Jensen (EN)
- Unicode Migration Resources for Delphi, C++Builder and RAD Studio (EN)
Vorhandene String-Typen
Die bereits vorhandenen Datentypen AnsiString
und System.WideString
arbeiten genauso wie bisher.
Bei kurzen Strings gibt es hinsichtlich der Funktionalität ebenfalls keine Änderungen. Beachten Sie bitte, dass kurze Strings auf 255 Zeichen beschränkt sind und nur einen Zeichenzähler und Einzelbyte-Zeichendaten enthalten. Sie umfassen keine Codeseiteninformationen. Ein kurzer String kann in Ausnahmenfällen UTF-8-Daten für eine bestimmte Anwendung enthalten.
AnsiString
string war früher ein Alias für AnsiString. Die folgende Tabelle zeigt die Position der Felder im früheren Format von AnsiString
:
Früheres Format des Datentyps AnsiString
:
Referenzzähler | Länge | String-Daten (auf Byte-Größe) | Nullterminiert |
---|---|---|---|
-8 |
-4 |
0 |
Length |
In RAD Studio wurde das Format von AnsiString geändert. Zwei neue Felder (CodePage
und ElemSize
) wurden hinzugefügt. Dadurch ist das Format von AnsiString identisch mit dem neuen Typ UnicodeString. (Weitere Informationen zum neuen Format finden Sie unter Lange String-Typen.)
WideString
System.WideString
wurde früher für Unicode-Zeichendaten verwendet. Das Format von WideString
entspricht im Wesentlichen dem von Windows BSTR
. WideString
sollte weiterhin in COM-Anwendungen verwendet werden.
Neuer String-Typ: UnicodeString
Der string
-Typ in RAD Studio ist der Typ UnicodeString
.
In Delphi sind Char
- und PChar
-Typen jetzt WideChar
- bzw. PWideChar
-Typen.
string
ein Alias für AnsiString
, und die Typen Char
und PChar
waren AnsiChar
bzw. PAnsiChar
.In C++ steuert die Option _TCHAR entspricht die Definition von _TCHAR
, das entweder wchar_t
oder char
sein kann.
RAD Studio Frameworks und Bibliotheken verwenden jetzt den Typ UnicodeString
und repräsentiert String-Werte nicht mehr als Einzelbyte- oder MBCS-Strings.
Format des Datentyps UnicodeString
:
Codeseite | Elementgröße | Referenzzähler | Länge | String-Daten (auf Elementgröße) | Nullterminiert |
---|---|---|---|---|---|
-12 |
-10 |
-8 |
-4 |
0 |
Length * elementsize |
UnicodeString
kann als die folgende Delphi-Struktur dargestellt werden:
type StrRec = record CodePage: Word; ElemSize: Word; refCount: Integer; Len: Integer; case Integer of 1: array[0..0] of AnsiChar; 2: array[0..0] of WideChar; end;
UnicodeString
fügt die Felder für die Codeseite CodePage
und die Elementgröße ElemSize
hinzu, die den Inhalt des Strings beschreiben. UnicodeString
ist zu allen anderen String-Typen zuweisungskompatibel. Zuweisungen zwischen AnsiString
und UnicodeString
führen aber weiterhin die entsprechenden Auf- oder Abkonvertierungen durch. Bitte beachten Sie, dass eine Zuweisung eines UnicodeString
-Typs zu einem AnsiString
-Typ nicht empfohlen wird und zu Datenverlusten führen kann.
Beachten Sie, dass AnsiString
ebenfalls über die Felder CodePage
und ElemSize
verfügt.
UnicodeString
-Daten sind aus folgenden Gründen in UTF-16 codiert:
- UTF-16 entspricht dem zugrunde liegenden Format des Betriebssystems.
- UTF-16 reduziert zusätzliche explizite/implizite Konvertierungen.
- Es bietet bei Aufrufen der Windows-API eine bessere Performanz.
- Es müssen keine Betriebssystem Konvertierungen mit UTF-16 durchgeführt werden.
- Der Basic Multilingual Plane (BMP) enthält bereits die Schriftzeichen fast aller aktiven Sprachen der Welt und passt in ein einzelnes UTF-16
Char
(16 Bits). - Unicode-Surrogatpaare entsprechen dem Multibyte-Zeichensatz (MBCS), sind aber berechenbarer und Standard.
UnicodeString
kann für das Marshalling von COM-Interfaces verlustlose implizite Konvertierungen in und ausWideString
bereitstellen.
Zeichen in UTF-16 können 2 oder 4 Byte groß sein, daher ist die Anzahl der Elemente in einem String nicht notwendigerweise gleich der Anzahl der Zeichen. Wenn der String nur aus BMP-Zeichen besteht, ist die Anzahl der Zeichen und der Elemente identisch.
UnicodeString
bietet die folgenden Vorteile:
- UnicodeString verfügt über einen Referenzzähler.
- UnicodeString löst ein Problem von Altanwendungen in C++Builder.
- Durch die Aufnahme von Codierungsinformationen (Codeseite) in
AnsiString
wird das Risiko eines möglichen Datenverlustes bei impliziten Typumwandlungen reduziert. - Der Compiler stellt sicher, dass die Daten korrekt sind, bevor die Daten verändert werden.
WideString
verfügt über keinen Referenzzähler, deshalb ist UnicodeString
für die meisten Anwendungstypen flexibler und effizienter (WideString
eignet sich eher für COM).
Indizierung
Instanzen von UnicodeString
können Zeichen indizieren. Die Indizierung ist 1-basiert, genau wie bei AnsiString
. Sehen Sie sich den folgenden Quelltext an:
var C: Char; S: string; begin ... C := S[1]; ... end;
In einem Fall, wie dem oben dargestellten, muss der Compiler sicher stellen, dass die Daten in S
in einem geeigneten Format vorliegen. Der Compiler erzeugt entsprechenden Code, damit Zuweisungen zu String-Elementen den richtigen Typ haben und die Instanz eindeutig ist (d.h., dass sie einen Referenzzähler von 1 hat). Dies geschieht über einen Aufruf der Funktion UniqueString
. Für den obigen Quelltext muss der Compiler, weil der String Unicode-Daten enthalten könnte, vor der Indizierung im Zeichen-Array auch die passende UniqueString
-Funktion aufrufen.
Compilerbedingungen
In Delphi als auch in C++Builder können Sie Bedingungen setzen, um im selben Quelltext Unicode- und Nicht-Unicode-Code zuzulassen.
Delphi
{$IFDEF UNICODE}
C++Builder
#ifdef _DELPHI_STRING_UNICODE
Überblick über die Änderungen
string
wird nunUnicodeString
und nichtAnsiString
zugeordnet.Char
wird nunWideChar
(2 Byte, nicht 1 Byte) zugeordnet und ist ein UTF-16-Zeichen.PChar
wird nunPWideChar
zugeordnet.- In C++ wird
System::String
nun der KlasseUnicodeString
zugeordnet.
Überblick über Unverändertes
AnsiString
.WideString
.AnsiChar
,PAnsiChar
.WideChar
,PWideChar
- Implizite Konvertierungen funktionieren weiterhin.
AnsiString
verwendet die aktive Codeseite des Benutzers.
Von der Zeichengröße unabhängige Quelltextkonstrukte
Die folgenden Operationen sind von der Zeichengröße unabhängig:
- String-Verkettung:
<string var> + <string var>
<string var> + <literal>
<literal> + <literal>
Concat(<string> , <string>)
- Standard-String-Funktionen:
Length(<string>)
gibt die Anzahl der char-Elemente zurück. Diese Anzahl muss nicht unbedingt gleich der Anzahl der Byte sein. Beachten Sie bitte, dass die FunktionSizeOf
die Anzahl der Byte zurückgibt, was bedeutet, dass sich der Rückgabewert fürSizeOf
vonLength
unterscheiden kann.Copy(<String>, <Start>, <Länge>)
gibt einen Teilstring inChar
-Elementen zurück.Pos(<Teilstring>, <String>)
gibt den Index des erstenChar
-Elements zurück.
- Operatoren:
<String> <Vergleichsoperator> <String>
CompareStr()
CompareText()
...
FillChar(<Struktur oder Arbeitsspeicher>)
FillChar(Rect, SizeOf(Rect), #0)
FillChar(WndClassEx, SizeOf(TWndClassEx), #0)
. Bitte beachten:WndClassEx.cbSize := SizeOf(TWndClassEx);
- Windows-API
- Bei API-Aufrufen werden standardmäßig deren
WideString
-(“W”)-Versionen verwendet. - Die
PChar(<String>)
-Typumwandlung hat eine identische Semantik.
- Bei API-Aufrufen werden standardmäßig deren
Beispiel für GetModuleFileName
:
function ModuleFileName(Handle: HMODULE): string; var Buffer: array[0..MAX_PATH] of Char; begin SetString(Result, Buffer, GetModuleFileName(Handle, Buffer, Length(Buffer))); end;
Beispiel für GetWindowText
:
function WindowCaption(Handle: HWND): string; begin SetLength(Result, 1024); SetLength(Result, GetWindowText(Handle, PChar(Result), Length(Result))); end;
Beispiel für die Zeichenindizierung von Strings:
function StripHotKeys(const S: string): string; var I, J: Integer; LastChar: Char; begin SetLength(Result, Length(S)); J := 0; LastChar := #0; for I := 1 to Length(S) do begin if (S[I] <> '&') or (LastChar = '&') then begin Inc(J); Result[J] := S[I]; end; LastChar := S[I]; end; SetLength(Result, J); end;
Von der Zeichengröße abhängige Quelltextkonstrukte
Einige Operationen sind von der Zeichengröße abhängig. Die Funktionen und Features in der folgenden Liste enthalten - sofern möglich - auch eine “portierbare” Version. Sie können Ihren Quelltext auch in der portierbaren Version neu schreiben, er funktioniert sowohl mit AnsiString
- als auch mit UnicodeString
-Variablen.
SizeOf(<Char-Array>)
- portierbare Version:Length(<Char-Array>)
.Move(<Char-Puffer>... CharCount)
- portierbare Version:Move(<Char-Puffer> ... CharCount * SizeOf(Char))
.- Stream Read/Write - portierbare Version:
AnsiString
,SizeOf(Char)
oder die KlasseTEncoding
. FillChar(<Char-Array>, <Größe>, <AnsiChar>)
- verwenden Sie zum Auffüllen mit#0
*SizeOf(Char)
oder die portierbareStringOfChar
-Funktion.GetProcAddress(<Modul>, <PAnsiChar>)
- verwenden Sie die überladene Funktion, die einPWideChar
übernimmt.- Typumwandlung oder die Verwendung von
PChar
für die Zeigerarithmetik - fügen Sie am Beginn der Datei{IFDEF PByte = PChar}
ein, wenn SiePChar
für die Zeigerarithmetik verwenden. Oder verwenden Sie die Delphi-Compiler-Direktive{POINTERMATH <ON|OFF>}
, um die Zeigerarithmetik für alle typisierten Zeiger zu aktivieren, damit das Inkrementieren/Dekrementieren nach Elementgröße ausgeführt wird.
Set of Char-Konstrukte
Sie müssen diese Konstrukte wahrscheinlich ändern.
- <Char> in <set of
AnsiChar
> - Codeerzeugung ist korrekt (>#255
-Zeichen sind nie in der Menge enthalten). Der Compiler gibt die WarnungWideChar in Set-Operationen verkürzt
aus. Abhängig von Ihrem Quelltext können Sie die Warnung gefahrlos deaktivieren. Verwenden Sie als Alternative die FunktionCharinSet
. - <Char> in
LeadBytes
- die globale MengeLeadBytes
ist für MBCS-ANSI-Gebietseinstellungen vorgesehen. UTF-16 arbeitet weiterhin mit einem "Führungszeichen" (lead char) (#$D800 - #$DBFF
sind hohe Surrogate,#$DC00 - #$DFFF
niedrige Surrogate). Verwenden Sie, um dies zu ändern, die überladene FunktionIsLeadChar
. Die ANSI-Version prüft aufLeadBytes
. DieWideChar
-Version prüft, ob es sich um ein hohes/niedriges Surrogat handelt. - Zeichenklassifikation - verwenden Sie die statische Klasse
TCharacter
. Die UnitCharacter
enthält Funktionen für die Klassifikation von Zeichen:IsDigit
,IsLetter
,IsLetterOrDigit
,IsSymbol
,IsWhiteSpace
,IsSurrogatePair
usw. Diese Funktionen basieren auf Tabellendaten der Unicode.org.
Achtung bei diesen Konstrukten
Sie sollten die folgenden problematischen Codekonstrukte untersuchen:
- Typumwandlungen, die den Typ verbergen:
AnsiString(Pointer(foo))
- Überprüfen Sie, was beabsichtigt war.
- Verdächtige Typumwandlungen erzeugen eine Warnung:
PChar(<AnsiString var>)
PAnsiChar(<UnicodeString var>)
- Direktes Erstellen, Manipulieren oder Zugreifen auf interne Strukturen. Einige dieser Strukturen, wie z.B.
AnsiString
, wurden intern verändert, daher sind diese Aktionen unsicher. Verwenden Sie die FunktionenStringRefCount
,StringCodePage
,StringElementSize
und andere, um String-Informationen zu ermitteln.
Laufzeitbibliothek
- Überladungen. Für Funktionen, die
PChar
übernehmen, gibt es jetztPAnsiChar
- undPWideChar
-Versionen, damit die richtige Funktion aufgerufen wird. AnsiXXX
-Funktionen sind möglich:SysUtils.AnsiXXXX
-Funktionen, wie z.B.AnsiCompareStr
:- Werden weiterhin mit
string
deklariert und verwendenUnicodeString
. - Bieten eine bessere Abwärtskompatibilität (Quelltext muss nicht geändert werden).
- Werden weiterhin mit
- Die
AnsiXXXX
-Funktionen der UnitAnsiStrings
bieten dieselben Möglichkeiten wie dieSysUtils.AnsiXXXX
-Funktionen, arbeiten aber nur mitAnsiString
. DieAnsiStrings.AnsiXXXX
-Funktionen bieten auch eine bessere Performanz für einenAnsiString
als dieSysUtils.AnsiXXXX
-Funktionen, die sowohl mitAnsiString
als auch mitUnicodeString
arbeiten, weil keine impliziten Konvertierungen ausgeführt werden.
Write/Writeln
undRead/Readln
:
PByte
- deklariert mit$POINTERMATH ON
. Dadurch wird die Array-Indizierung und die Zeigermathematik wie beiPAnsiChar
ermöglicht.
- String-Informationsfunktionen:
StringElementSize
gibt die aktuelle Datengröße zurück.StringCodePage
gibt die Codeseite von String-Daten zurück.System.StringRefCount
gibt den Referenzzähler zurück.
- Die RTL stellt Hilfsfunktionen bereit, mit denen Anwender explizite Konvertierungen zwischen Codeseiten und Elementgrößenumwandlungen vornehmen können. Wenn Entwickler die Funktion
Move
für ein Zeichen-Array verwenden, können sie keine Annahme über die Elementgröße machen. Dieses Problem kann reduziert werden, indem sichergestellt wird, dass alle R-Wert-Referenzen korrekte Aufrufe der RTL erzeugen, um korrekte Elementgrößen zu gewährleisten.
Komponenten und Klassen
TStrings
: SpeichertUnicodeString
intern (bleibt alsstring
deklariert).TWideStrings
(wird eventuell bald nicht mehr unterstützt) ist unverändert. VerwendetWideString
(BSTR) intern.TStringStream
- Wurde neu geschrieben – hat per Vorgabe die ANSI-Standardcodierung für interne Speicherung.
- Die Codierung kann überschrieben werden.
- Ziehen Sie zum Erstellen eines Strings aus verschiedenen Teilen
TStringBuilder
anstelle vonTStringStream
in Betracht.
TEncoding
- Verwendet standardmäßig die aktive Codeseite des Anwenders.
- Unterstützt UTF-8.
- Unterstützt UTF-16, Big und Little Endian.
- Unterstützt Byte Order Mark (BOM).
- Sie können für benutzerspezifische Codierungen abgeleitete Klassen erstellen.
- Komponenten-Streaming (Text-DFM-Dateien):
- Sind vollständig abwärtskompatibel.
- Verwenden für das Streaming nur UTF-8, wenn der Komponententyp, die Eigenschaft oder der Name keine ASCII-7-Zeichen enthält.
- String-Eigenschaftswerte werden weiterhin mit dem “#”-Escaped-Format in den Stream gestellt.
- Lassen auch Werte als UTF-8 zu (noch nicht geklärtes Problem).
- Nur die Änderung im Binärformat ist für UTF-8-Daten für Komponentennamen, Eigenschaften und Typnamen möglich.
Byte Order Mark
Das Byte Order Mark (BOM) sollte zur Angabe der Codierung Dateien hinzugefügt werden:
- UTF-8 verwendet
EF BB BF
. - UTF-16 Little Endian verwendet
FF FE
. - UTF-16 Big Endian verwendet
FE FF
.
Schritte zum Aktivieren von Unicode in Anwendungen
Sie müssen die folgenden Schritte ausführen:
- Überprüfen von Char- und String-bezogenen Funktionen
- Neu erzeugen der Anwendung
- Überprüfen von Surrogatpaaren
- Überprüfen der String-Nutzdaten
Weitere Einzelheiten finden Sie unter Anwendungen für Unicode anpassen.
Neue Warnungen des Delphi-Compilers
Dem Delphi-Compiler wurden neue Warnungen hinzugefügt, die sich auf mögliche Fehler bei der Typumwandlung beziehen (z.B. beim Umwandeln eines UnicodeString
- oder WideString
-Strings in AnsiString
oder AnsiChar
). Wenn Sie eine Anwendung nach Unicode konvertieren, sollten Sie die Warnungen 1057 und 1058 aktivieren, die Sie beim Auffinden von Problembereichen in Ihrem Quelltext unterstützen.
- W1050: WideChar in Set-Ausdrücken auf ByteChar verkürzt (Delphi). "Set of char" definiert in Win32 eine Menge über den gesamten Bereich des Char-Typs. Da Char in Win32 ein Typ auf Byte-Größe ist, wird dadurch eine Menge mit maximaler Größe und mit 256 Elementen definiert. In .NET ist Char ein Typ auf Word-Größe, dessen Bereich (0..65535) die Kapazität des Mengen-Typs überschreitet.
- W1057: Implizite String-Umwandlung von '%s' zu '%s' (IMPLICIT_STRING_CAST) Wird ausgegeben, wenn der Compiler einen Fall findet, bei dem ein
AnsiString
(oder einAnsiChar
) in eine Form von Unicode (einenUnicodeString
oder einenWideString
) implizit konvertiert werden muss. (HINWEIS: Diese Warnung ist eventuell per Vorgabe aktiviert). - W1058: Implizite String-Umwandlung mit potenziellem Datenverlust von '%s' zu '%s' (IMPLICIT_STRING_CAST_LOSS) Wird ausgegeben, wenn der Compiler einen Fall findet, bei dem eine Form von Unicode (ein
UnicodeString
oder einWideString
) in einenAnsiString
(oder einAnsiChar
) implizit konvertiert werden muss. Dies ist eine potenziell verlustreiche Umwandlung, weil es Zeichen in dem String geben kann, die auf der Codeseite, in die der String umgewandelt wird, nicht repräsentiert werden können. (HINWEIS: Diese Warnung ist eventuell per Vorgabe aktiviert). - W1059: Explizite String-Umwandlung von '%s' zu '%s' (EXPLICIT_STRING_CAST) Wird ausgegeben, wenn der Compiler auf einen Fall trifft, bei dem der Programmierer explizit einen
AnsiString
(oder einAnsiChar
) in eine Form von Unicode (UnicodeString
oderWideString
) umwandelt. (HINWEIS: Diese Warnung ist standardmäßig deaktiviert und sollte nur zum Auffinden eventueller Probleme verwendet werden.) - W1060: Explizite String-Umwandlung mit potenziellem Datenverlust von '%s' zu '%s' (EXPLICIT_STRING_CAST_LOSS) Wird ausgegeben, wenn der Compiler einen Fall findet, bei dem der Programmierer explizit eine Form von Unicode (
UnicodeString
oderWideString
) in einenAnsiString
(oder einAnsiChar
) konvertiert. Dies ist eine potenziell verlustreiche Umwandlung, weil es Zeichen in dem String geben kann, die auf der Codeseite, in die der String umgewandelt wird, nicht repräsentiert werden können. (HINWEIS: Diese Warnung ist standardmäßig deaktiviert und sollte nur zum Auffinden eventueller Probleme verwendet werden.)
Empfehlungen
- Belassen Sie die Quelltextdaten im UTF-8-Format:
- Dateien können im ANSI-Format bleiben, wenn der Quelltext mit der korrekten Codeseite compiliert wird. Wählen Sie (Projekt > Optionen > C++-Compiler > Erweitert und verwenden Sie die Option "Codeseite" unter Weitere Optionen, um die korrekte Codeseite festzulegen.
- Schreiben Sie in die Quelltextdatei ein UTF-8 BOM. Stellen Sie sicher, dass Ihr Versionsverwaltungssystem diese Dateien unterstützt.
- Führen Sie in der IDE ein Refactoring durch, wenn der Quelltext
AnsiString
oderAnsiChar
sein muss (der Quelltext ist weiterhin portierbar). - Statische Überprüfung des Quelltextes:
- Gibt der Quelltext Daten nur weiter?
- Führt der Quelltext eine einfache Zeichenindizierung durch?
- Beachten Sie alle Warnungen (es könnten Fehler daraus werden):
- Verdächtige Zeigerumwandlungen.
- Implizite/explizite Umwandlungen.
- Bestimmen Sie den Zweck des Quelltextes.
- Verwendet der Quelltext einen String (
AnsiString
) als ein dynamisches Byte-Array? Wenn ja, dann setzen Sie stattdessen den portierbaren TypTBytes
(Byte
-Array) ein. - Wird mit einer
PChar
-Umwandlung die Zeigerarithmetik aktiviert? Wenn ja, wandeln Sie stattdessen inPByte
um, und aktivieren Sie$POINTERMATH ON
.
- Verwendet der Quelltext einen String (
Siehe auch
- UTF-8-Konvertierungsroutinen
- Delphi Unicode Migration for Mere Mortals: Stories and Advice from the Front Lines von Cary Jensen (EN)
- Delphi in a Unicode World Part I: What is Unicode, Why do you need it, and How do you work with it in Delphi? (EN)
- Delphi in a Unicode World Part II: New RTL Features and Classes to Support Unicode (EN)
- RAD Studio 2010 Migration Center (EN)
- Anwendungen für Unicode anpassen
- C++-Anwendungen für Unicode anpassen
- _TCHAR-Zuordnung (C++)
- Verwendung von Unicode in der Befehlskonsole
- Verwendung von TEncoding für Unicode-Dateien
- Behandlung der AnsiString-Codeseitenspezifikation in C++
- System.UnicodeString
- System.AnsiString