Unicode dans RAD Studio

De RAD Studio
Aller à : navigation, rechercher

Remonter à Introduction à RAD Studio


RAD Studio utilise les chaînes ANSI, c'est-à-dire, le type string est désormais une chaîne Unicode (System.UnicodeString), non plus une chaîne ANSI. Cette rubrique décrit ce que vous devez savoir pour gérer correctement les chaînes.

Si vous voulez utiliser des chaînes ANSI ou des chaînes étendues, utilisez les types AnsiString and WideString.

RAD Studio est entièrement compatible Unicode, et des modifications pourraient être requises sur les parties de votre code qui impliquent la gestion de chaînes. Toutefois, tous les efforts ont été effectués pour minimiser ces modifications. Bien que de nouveaux types de données ont été introduits, les types de données existants ont été conservés et ils fonctionnent comme auparavant. Grâce à l'expérience maison de la conversion Unicode, les applications de développeurs existantes devraient migrer assez bien.

Pour des ressources supplémentaires  :

Types de chaînes existants

Les types de données pré-existants AnsiString et System.WideString fonctionnent comme auparavant.

Les chaînes courtes fonctionnent aussi comme auparavant. Notez que les chaînes courtes sont limitées à 255 caractères et contiennent seulement un nombre de caractères et des données caractère à octet unique. Elles ne contiennent pas des informations de page de code. Une chaîne courte peut contenir des données UTF-8 pour une application particulière, mais ce n'est généralement pas le cas.

AnsiString

Auparavant, string était un alias pour AnsiString. Ce tableau présente l'emplacement des champs au format précédent de AnsiString :

Format précédent du type de données AnsiString :

Comptage de références Longueur Données chaîne (en octets) Terme Null
-8
-4
0
Length

Pour RAD Studio, le format de AnsiString a changé. Deux nouveaux champs (CodePage et ElemSize) ont été ajoutés. Cela rend le format deAnsiString identique pour le nouveau type UnicodeString. (Pour davantage d'informations sur ce nouveau format, voir Types chaîne longue.)

WideString

System.WideString était auparavant utilisé pour les données caractères Unicode. Son format est essentiellement le même que le BSTR Windows. WideString est toujours approprié dans les applications COM.

Nouveau type de chaîne : UnicodeString

Le type string de RAD Studio est le type UnicodeString.

Pour Delphi, les types Char et PChar sont maintenant respectivement WideChar et PWideChar.

Remarque: Cela diffère des versions antérieures à 2009, dans lesquelles string était un alias pour AnsiString, et les types Char et PChar étaient respectivement AnsiChar et PAnsiChar.

Pour C++, l'option Mappage de _TCHAR en contrôle la définition flottante de _TCHAR, qui peut être wchar_t ou char.

Les frameworks et bibliothèques de RAD Studio utilisent désormais le type UnicodeString ; ils ne représentent plus les valeurs de chaînes sous forme de chaînes sur un seul octet ou MBCS.

Format du type de données UnicodeString :

Page de code Taille d'élément Comptage de références Longueur Données chaîne (en éléments) Terme Null
-12
-10
-8
-4
0
Length * elementsize


UnicodeString peut être représenté comme la structure Delphi suivante :

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 ajoute la page de code CodePage et les champs de taille d'élément ElemSize qui décrivent le contenu de la chaîne. UnicodeString est compatible en assignation à tous les autres types chaîne. Toutefois, les assignations entre AnsiString et UnicodeString effectuent toujours les conversions appropriées vers le bas et vers le haut. Notez que l'assignation d'un type UnicodeString à un type AnsiString n'est pas recommandée et peut générer une perte de données.

Notez que AnsiString a aussi les champs CodePage et ElemSize.

Les données UnicodeString sont au format UTF-16 pour les raisons suivantes  :

  • UTF-16 correspond au format du système d'exploitation sous-jacent.
  • UTF-16 réduit les conversions explicites/implicites supplémentaires.
  • Il offre de meilleures performances lors de l'appel de l'API Windows.
  • Avec UTF-16, le système d'exploitation n'a pas besoin d'effectuer des conversions.
  • Le BMP (Basic Multilingual Plane) contient déjà la vaste majorité des glyphes de langage actifs dans le monde et tient dans un Char UTF-16 (16 bits) unique.
  • Les paires de substitution Unicode sont analogues au jeu de caractères multi-octets (MBCS), mais plus prévisibles et standard.
  • UnicodeString peut fournir des conversions implicites sans perte, vers et depuis WideString pour les interfaces COM de marshaling.

Les caractères en UTF-16 peuvent occuper 2 ou 4 octets. Ainsi, le nombre des éléments d'une chaîne n'est pas nécessairement égal au nombre de caractères. Si la chaîne ne comporte que des caractères BMP, le nombre de caractères et le nombre d'éléments sont égaux.

UnicodeString offre les avantages suivants  :

  • Il utilise le comptage de références.
  • Il résout un ancien problème d'application dans C++Builder.
  • L'emploi de AnsiString pour transporter les informations d'encodage (page de code) réduit le problème de perte de données potentiel avec les transtypages implicites.
  • Le compilateur garantit que les données sont correctes avant la mutation des données.

WideString n'utilise pas le comptage de références, et UnicodeString est ainsi plus flexible et efficace dans la plupart des types d'applications (WideString est plus approprié à COM).

Indexation

Les instances de UnicodeString peuvent indexer les caractères. L'indexation est à base 1, comme pour AnsiString. Considérons le code suivant  :

var C: Char;
    S: string;
    begin
        ...
        C := S[1];
        ...
    end;

Dans un cas comme celui présenté ci-dessus, le compilateur doit s'assurer que les données de S sont au format approprié. Le compilateur génère du code pour s'assurer que les assignations aux éléments chaîne ont le type approprié et que l'instance est unique (c'est-à-dire que le nombre de références est égal à 1) via un appel à une fonction UniqueString. Pour le code ci-dessus, puisque la chaîne peut contenir des données Unicode, le compilateur doit aussi appeler la fonction UniqueString appropriée avant l'indexation dans le tableau de caractères.


Conditions du compilateur

Dans Delphi et C++Builder, vous pouvez utiliser des conditions pour autoriser le code Unicode et le code non-Unicode dans le même source.

Delphi

{$IFDEF UNICODE}

C++Builder

#ifdef _DELPHI_STRING_UNICODE 

Résumé des modifications

  • string est maintenant mappé en UnicodeString, pas en AnsiString.
  • Char est maintenant mappé en WideChar (2 octets, pas 1 octet) et est un caractère UTF-16.
  • PChar est maintenant mappé en PWideChar.
  • Dans C++, System::String est maintenant mappé en classe UnicodeString.

Résumé de ce qui n'a pas changé

  • AnsiString.
  • WideString.
  • AnsiChar, PAnsiChar.
  • WideChar, PWideChar
  • Les conversions implicites fonctionnent toujours.
  • AnsiString utilise la page de code active de l'utilisateur.

Constructions de code indépendantes de la taille des caractères

Les opérations suivantes ne dépendent pas de la taille des caractères  :

  • Concaténation de chaînes
    • <var chaîne> + <var chaîne>
    • <var chaîne> + <littéral>
    • <littéral> + <littéral>
    • Concat(<chaîne> , <chaîne>)
  • Fonctions chaîne standard
    • Length(<chaîne>) renvoie le nombre d'éléments char, qui peut être différent du nombre d'octets. Notez que la fonction SizeOf renvoie le nombre d'octets, ce qui signifie que la valeur de retour de SizeOf peut différer de celle de Length.
    • Copy(<chaîne>, <début>, <longueur>) renvoie une sous-chaîne d'éléments Char.
    • Pos(<sous-chaîne>, <chaîne>) renvoie l'index du premier élément Char.
  • Opérateurs
    • <chaîne> <opérateur_comparaison> <chaîne>
    • CompareStr()
    • CompareText()
    • ...
  • FillChar(<structure ou mémoire>)
    • FillChar(Rect, SizeOf(Rect), #0)
    • FillChar(WndClassEx, SizeOf(TWndClassEx), #0). Notez que WndClassEx.cbSize := SizeOf(TWndClassEx);
  • API Windows
    • Les appels API sont initialisés par défaut à leurs versions WideString ("W").
    • Le transtypage PChar(<chaîne>) a une sémantique identique.


Exemple GetModuleFileName :

function ModuleFileName(Handle: HMODULE): string;
    var Buffer: array[0..MAX_PATH] of Char;
        begin
            SetString(Result, Buffer, 
                      GetModuleFileName(Handle, Buffer, Length(Buffer)));
        end;

Exemple GetWindowText :

function WindowCaption(Handle: HWND): string;
      begin
          SetLength(Result, 1024);
          SetLength(Result, 
                    GetWindowText(Handle, PChar(Result), Length(Result)));
      end;

Exemple d'indexation de caractères chaîne :

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;

Constructions de code dépendantes de la taille des caractères

Certaines opérations dépendent de la taille des caractères. Les fonctions et les fonctionnalités de la liste suivante incluent aussi une version "portable" quand c'est possible. Vous pouvez de façon similaire réécrire votre code afin qu'il soit portable, c'est-à-dire qu'il fonctionne à la fois avec les variables AnsiString et UnicodeString.

  • SizeOf(<tableau car>) - utilisez la fonction portable Length(<tableau car>).
  • Move(<tampon car>... CharCount) - utilisez la fonction portable Move(<tampon car> ... CharCount * SizeOf(Char)).
  • Stream Read/Write - utilisez la fonction portable AnsiString, SizeOf(Char) ou la classe TEncoding.
  • FillChar(<tableau car>, <taille>, <AnsiChar>) - utilisez la fonction *SizeOf(Char) si remplissage par #0, ou la fonction portable StringOfChar.
  • GetProcAddress(<module>, <PAnsiChar>) - utilisez la fonction de surcharge fournie prenant un PWideChar.
  • Transtypage ou utilisation de PChar pour l'arithmétique de pointeur - Placez {IFDEF PByte = PChar} au début du fichier si vous utilisez PChar pour l'arithmétique de pointeur. Ou bien utilisez la directive du compilateur Delphi {POINTERMATH <ON|OFF>} pour activer l'arithmétique de pointeur pour tous les pointeurs typés, afin que l'incrémentation / décrémentation se fasse par taille d'élément.

Constructions Set of Char

Vous pouvez modifier ces constructions.

  • <Char> in <set of AnsiChar> - la génération du code est correcte (les caractères >#255 ne sont jamais dans l'ensemble). Le compilateur affiche l'avertissement WideChar réduit dans les opérations d'ensemble. Selon votre code, vous pouvez désactiver en toute sécurité l'avertissement. Vous pouvez aussi utiliser la fonction CharinSet.
  • <Char> in LeadBytes - l'ensemble LeadBytes global est pour les locales MBCS ANSI. UTF-16 a toujours la notion d'un "lead char" (#$D800 - #$DBFF : substituts étendus, #$DC00 - #$DFFF : substituts faibles). Pour changer cela, utilisez la fonction surchargée IsLeadChar. La version ANSI est confrontée à LeadBytes. La version WideChar vérifie si c'est un substitut faible / étendu.
  • Classification des caractères - utilisez la classe statique TCharacter. L'unité Character offre des fonctions pour classifier les caractères : IsDigit, IsLetter, IsLetterOrDigit, IsSymbol, IsWhiteSpace, IsSurrogatePair, et ainsi de suite. Elles sont basées sur les données de table directement de Unicode.org.

Méfiez-vous de ces constructions

Vous devriez examiner les constructions de code problématiques suivantes  :

  • Transtypages qui masquent le type
    • AnsiString(Pointer(foo))
    • Revoir pour correction : qu'était-il prévu ?
  • Transtypages suspects - génèrent un avertissement
    • PChar(<AnsiString var>)
    • PAnsiChar(<UnicodeString var>)
  • Construction, manipulation ou accès direct aux structures internes de chaînes. Certaines fonctions, comme AnsiString, ont été modifiées en interne, ce n'est donc pas sécurisé. Utilisez StringRefCount, StringCodePage, StringElementSize et d'autres fonctions pour obtenir des informations de chaîne.

Bibliothèque d'exécution

  • Surcharges. Pour les fonctions qui prennent PChar, il existe maintenant les versions PAnsiChar et PWideChar afin que la fonction appropriée soit appelée.
  • Les fonctions AnsiXXX sont prises en considération
    • Fonctions SysUtils.AnsiXXXX, telles que AnsiCompareStr
      • Reste déclaré avec string et migre vers UnicodeString.
      • Offre une meilleure compatibilité descendante (pas besoin de changer le code).
    • Les fonctions AnsiXXXX de l'unité AnsiStrings offrent les mêmes capacités que les fonctions SysUtils.AnsiXXXX, mais elles fonctionnent seulement pour AnsiString. En outre, les fonctions AnsiStrings.AnsiXXXX fournissent de meilleures performances pour un AnsiString que les fonctions SysUtils.AnsiXXXX, qui fonctionnent pour AnsiString et UnicodeString, car aucune conversion implicite n'est effectuée.
  • Write/Writeln et Read/Readln
    • Continue à convertir vers / depuis les pages de codes ANSI/OEM.
    • Console est surtout ANSI ou en tout cas OEM.
    • Offre une meilleure compatibilité avec les applications anciennes.
    • TFDD (Pilotes de périphérique de fichiers texte)
      • TTextRec et TFileRec.
      • Les noms de fichier sont WideChar, mais comme ci-dessus, les données sont ANSI/OEM.
    • Utilisez TEncoding et TStrings pour les E/S de fichiers Unicode.
  • PByte - déclarée avec $POINTERMATH ON. Cela permet l'indexation de tableau et la fonction mathématique de pointeur comme PAnsiChar.
  • La RTL fournit des fonctions d'assistance qui permettent aux utilisateurs de faire des conversions explicites entre les pages de codes et les conversions de taille d'éléments. Si les développeurs utilisent la fonction Move sur un tableau de caractères, ils ne peuvent pas faire de supposition sur la taille des éléments. Ce problème peut être atténué en s'assurant que toutes les références RValue génèrent les appels appropriés à RTL pour garantir des tailles d'éléments correctes.

Composants et classes

  • TStrings : Stocke UnicodeString en interne (reste déclaré comme string).
  • TWideStrings (dépréciable) est inchangé. Utilise WideString (BSTR) en interne.
  • TStringStream
    • A été réécrite - initialisée à l'encodage ANSI par défaut pour le stockage interne.
    • L'encodage peut être redéfini.
    • Considérez l'utilisation de TStringBuilder au lieu de TStringStream pour construire une chaîne à partir de morceaux.
  • TEncoding
    • Initialisée par défaut à la page de code active de l'utilisateur.
    • Prend en charge UTF-8.
    • Prend en charge UTF-16, Big Endian et Little Endian.
    • Prise en charge de de BOM (Byte Order Mark).
    • Vous pouvez créer des classes descendantes pour les encodages spécifiques aux utilisateurs.
  • Flux de composants (fichiers DFM texte)
    • Compatibilité descendante complète.
    • Flux en UTF-8 seulement si le type de composant, la propriété ou le nom contient des caractères non-ASCII-7.
    • Les valeurs des propriétés de chaînes sont toujours mises en flux au format d'échappement "#".
    • Peut aussi autoriser les valeurs en UTF-8.
    • Seule la modification au format binaire est potentielle pour les données UTF-8 pour le nom de composant, les propriétés et le nom de type.

BOM (Byte Order Mark)

Le BOM (Byte Order Mark) doit être ajouté aux fichiers pour indiquer leur encodage  :

  • UTF-8 utilise EF BB BF.
  • UTF-16 Little Endian utilise FF FE.
  • UTF-16 Big Endian utilise FE FF.

Etapes d'activation pour Unicode de vos applications

Vous devez effectuer ces étapes :

  1. Revoir les fonctions relatives aux chaînes et aux caractères.
  2. Reconstruire l'application.
  3. Revoir les paires de substitution.
  4. Revoir les charges utiles des chaînes.

Pour de plus amples détails, voir Activation de vos applications pour Unicode.

Nouveaux avertissements du compilateur Delphi

De nouveaux avertissements ont été ajoutés au compilateur Delphi concernant des erreurs possibles de transtypage (par exemple, depuis un UnicodeString ou un WideString vers un AnsiString ou AnsiChar). Quand vous convertissez une application en Unicode, vous devez activer les avertissements 1057 et 1058 pour obtenir de l'assistance dans la recherche des zones de problèmes de votre code.

Recommandations

  • Conservez les fichiers source au format UTF-8
    • Les fichiers peuvent rester ANSI aussi longtemps que le source est compilé avec la page de code correcte. Sélectionnez Projet > Options > Compilateur C++ > Options avancées et utilisez l'option "Page de code" sous Autres options pour définir la page de code correcte.
    • Ecrivez un BOM UTF-8 dans le fichier source. Assurez-vous que le système de gestion du contrôle de source prend en charge ces fichiers.
  • Effectuez un refactoring EDI quand le code doit être AnsiString ou AnsiChar (code toujours portable).
  • Révision du code statique
    • Le code passe t-il simplement les données ?
    • Le code fait-il une simple indexation de caractères ?
  • Tenez compte de tous les avertissements (élever en erreurs)
    • Conversions de pointeur suspectes.
    • Conversions implicites/explicites.
  • Déterminez l'objectif du code
    • Le code utilise t-il une chaîne (AnsiString) comme un tableau d'octets dynamique ? Dans ce cas, utilisez à la place le type TBytes portable (tableau de Byte).
    • Une conversion PChar est-elle utilisée pour activer l'arithmétique de pointeur ? Dans ce cas, convertissez plutôt en PByte et effectuez l'activation $POINTERMATH ON.

Voir aussi