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
- RAD Studio 2010 Migration Center
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.
Hinweis: Dies ist ein Unterschied zu Versionen vor 2009. In früheren Versionen war
stringein Alias fürAnsiString, und die TypenCharundPCharwarenAnsiCharbzw.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.
UnicodeStringkann für das Marshalling von COM-Interfaces verlustlose implizite Konvertierungen in und ausWideStringbereitstellen.
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
AnsiStringwird 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
stringwird nunUnicodeStringund nichtAnsiStringzugeordnet.Charwird nunWideChar(2 Byte, nicht 1 Byte) zugeordnet und ist ein UTF-16-Zeichen.PCharwird nunPWideCharzugeordnet.- In C++ wird
System::Stringnun der KlasseUnicodeStringzugeordnet.
Überblick über Unverändertes
AnsiString.WideString.AnsiChar,PAnsiChar.WideChar,PWideChar- Implizite Konvertierungen funktionieren weiterhin.
AnsiStringverwendet 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 FunktionSizeOfdie Anzahl der Byte zurückgibt, was bedeutet, dass sich der Rückgabewert fürSizeOfvonLengthunterscheiden 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
PCharfür die Zeigerarithmetik - fügen Sie am Beginn der Datei{IFDEF PByte = PChar}ein, wenn SiePCharfü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ürztaus. Abhängig von Ihrem Quelltext können Sie die Warnung gefahrlos deaktivieren. Verwenden Sie als Alternative die FunktionCharinSet. - <Char> in
LeadBytes- die globale MengeLeadBytesist für MBCS-ANSI-Gebietseinstellungen vorgesehen. UTF-16 arbeitet weiterhin mit einem "Führungszeichen" (lead char) (#$D800 - #$DBFFsind hohe Surrogate,#$DC00 - #$DFFFniedrige 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 UnitCharacterenthält Funktionen für die Klassifikation von Zeichen:IsDigit,IsLetter,IsLetterOrDigit,IsSymbol,IsWhiteSpace,IsSurrogatePairusw. 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,StringElementSizeund 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
stringdeklariert und verwendenUnicodeString. - Bieten eine bessere Abwärtskompatibilität (Quelltext muss nicht geändert werden).
- Werden weiterhin mit
- Die
AnsiXXXX-Funktionen der UnitAnsiStringsbieten dieselben Möglichkeiten wie dieSysUtils.AnsiXXXX-Funktionen, arbeiten aber nur mitAnsiString. DieAnsiStrings.AnsiXXXX-Funktionen bieten auch eine bessere Performanz für einenAnsiStringals dieSysUtils.AnsiXXXX-Funktionen, die sowohl mitAnsiStringals auch mitUnicodeStringarbeiten, weil keine impliziten Konvertierungen ausgeführt werden.
Write/WritelnundRead/Readln:
PByte- deklariert mit$POINTERMATH ON. Dadurch wird die Array-Indizierung und die Zeigermathematik wie beiPAnsiCharermöglicht.
- String-Informationsfunktionen:
StringElementSizegibt die aktuelle Datengröße zurück.StringCodePagegibt die Codeseite von String-Daten zurück.System.StringRefCountgibt 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
Movefü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: SpeichertUnicodeStringintern (bleibt alsstringdeklariert).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
TStringBuilderanstelle vonTStringStreamin 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.
- 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 (einenUnicodeStringoder 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
UnicodeStringoder 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 (UnicodeStringoderWideString) 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 (
UnicodeStringoderWideString) 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
AnsiStringoderAnsiCharsein 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 inPByteum, 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
- 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