- r駸um? : Delphi 2009 a introduit Unicode comme le type String par D馭aut. Cet article pr駸ente Unicode, les types String Delphi avant et apr鑚 Delphi 2009, et pr駸ente les instructions qui devront 黎re modifi馥s pour migrer un projet Delphi vers les versions 2009 et suivantes
 
- mots cl? : Unicode - Ansi - Oem - Utf-8 - Utf-16 - composites - surrogate - tCharacter - tEncoding
 
- logiciel utilis? : Windows XP personnel, Delphi 2010
 
- mat駻iel utilis? : Pentium 2.800 Mhz, 512 Meg de m駑oire, 250 Giga disque dur
 
- champ d'application : Delphi 1 ? 5, Delphi 6, Delphi 7, Delphi 2006, Turbo Delphi, Delphi 2007 sur Windows, Delphi 2009, 2010, Delphi XE
 
- niveau : d騅eloppeur Delphi
 
- plan :
 
Ce basculement offre des avantages certains:
 
Compte tenu des missions de migration que nous ont confi? nos clients, nous pouvons consid駻er trois cas: 
 
 En r駸um? 
 
 Comme l'ensemble tenait en moins de 128 codes, IBM d馗ida d'utiliser les codes restants, entre 128 et 255 pour repr駸enter 
 
 Toutefois, les pays non latins (les grecs, les arrabes, les russes, sans parler des japonais ou des chinois) souhaitaient utiliser les 128 caract鑽es disponibles pour coder leurs jeux de caract鑽es ? eux. 
De ce fait
 
 
En revanche, le "a minuscule", "? accent grave", "? circonflexe" etc ont chacun leur propre code point
Le code est officiellement not? avec un pr馭ixe "U+" suivi de la valeur en hexad馗imal
U+0041
LATIN CAPITAL LETTER A
et pour le "a" (code point U+0061) :
LATIN SMALL LETTER A
La page Nom des code points vous donnera une id馥 des codes et des noms utilis駸
 
Et le dessin (appel? script) de chaque caract鑽e est pr駸ent? dans des fichier .PDF, r駱artis en langue, type de symbole etc.
 
 
De la m麥e fa輟n, les codes points Unicode ont 騁? class駸 en 7 cat馮ories fondamentales: Graphic, Format, Control, Private-Use, Surrogate, Noncharacter, Reserved.
Les cat馮ories autres que les lettres 騁endent Unicode pour 馗rire des documents scientifiques, des pages de musique, des illustrations symboliques (POLICE OFFICER U+1F46E, PRINCESS U+1F478, RAT U+1F400 !). Plus le Braille, les dominos, le mahjong, l'alchimie ...
 
 
Pour la plage des 64 K premi鑽es valeurs la d馗omposition se pr駸ente ainsi:
 
 Vous constaterez que la plupart des caract鑽es que nous utilisons couramment
(lettres latines et des principaux alphabets) sont au d饕ut de la plage des valeurs. 
Et les codes dont la valeur est inf駻ieure ? 64 K est appel? le Basic Multilingual Plane (plan multilingue de base), ou BMP. Les code points que nous utiliserons couremment seront dans la plage du BMP. Mais il n'en demeure pas moins que les code points sont d馭inis sur 4 octets.
 
 Les autres "plans" dont nous parlerons fort peu, contiennent, par exemple 
Nous nous limiterons essentiellement au plan 0, BMP.
 
 
U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX (donc "?")
0061 0302
 C'est la version "d馗ompos馥" ou "composite"
 
Et cela peut se corser avec des compositions multiples:
qui est, comme chacun sait, diff駻ent de
 
 Le probl鑪e se pose alors pour g駻er les cha?nes contenant ces caract鑽es composites 
Les 3 m騁hodes de codage le plus fr駲uemment utilis馥s sont UTF-8, UTF-16 et UTF-32 (UTF: Unicode Transformation Standard). Il en existe d'autres tels que UCS-2 ou UCS-4 (Universal Character Set), moins utilis馥s actuellement.
 
 
Dans cette repr駸entation :
A titre d'exemple
 
 
En revanche, j'ai pu afficher:
 
 
 
 
La sp馗ification de cet ordre est importante pour stocker les donn馥s dans des fichiers. En fait les fichiers peuvent optionnellement comporter une signature, en d饕ut de fichier, qui pr馗ise cet ordre. Cette signature, et est appell馥 BOM (Byte Order Mark) et a les valeurs suivantes:
Et
Lorsque le pChar pointe vers une "StringZ" Windows, la suite des caract鑽es est termin馥 par un z駻o. Mais nous pouvons faire pointer un pChar vers n'importe quoi (c'est un pointeur de type ^Char, avec des fonctionalit駸 de traitement de cha?nes, comme la longueur, utilisables si nous pointons vers une v駻itable "StringZ", et dot? d'une s駑antique de pointeur C, donc avec possibilit? de recalculer d'adresse par "+")
Ce type 騁ait ? l'origine utilis?
Procedure TForm1.String_Click(Sender: TObject);
 Var l_string: String;
   l_index: integer;
    l_result: String;
 Begin
   display('String (=AnsiString) avec e aigu et i traema'
  l_string:= 'abc 鴆';
 
  With t_pt_delphi_6_string_header(Integer(l_string)- 8) ^ Do
  Begin
   display('ref '+ Format('%4x', [m_reference_count]));
   display('byte count '+ Format('%4x', [m_byte_count]));
   l_result:= '';
   For l_index:= 0 To m_byte_count- 1 Do
    (*$r-*)
    l_result:= l_result+ Format('%1x ', [m_bytes[l_index]]);
    (*$r+*)
    display(l_result);
  End;
  End; // String_Click
 
soit (la r馭駻ence d'usage est -1 car la String est locale)
01_string_delphi_6
 
 Et voici un exemple d'affichage Unicode en Delphi 6 
 Procedure TForm1.WideString_Click(Sender: TObject);
 Const k_u_e_grave= $E8;
     k_u_heart= 2665ドル;
 Var l_wide_string: WideString;
   l_index: Integer;
    l_result: String;
 Begin
   display('WideString e grave, chr(4), e aigu, A');
  SetLength(l_wide_string, 5);
  l_wide_string[1]:= WideChar(k_u_e_grave);
  l_wide_string[2]:= WideChar(k_u_heart);
  // -- try surrogates for ?
   l_wide_string[3]:= WideChar(0065ドル);
  l_wide_string[4]:= WideChar(0301ドル);
  l_wide_string[5]:= WideChar(0041ドル);
 
  // -- affiche le contenu de la WideString
  With t_pt_widestring_delphi_6(Integer(l_wide_string)- 4)^ Do
  Begin
   display(' byte_count '+ IntToStr(m_byte_length));
   display(' Length '+ IntToStr(Length(l_wide_string)));
   l_result:= '';
   For l_index:= 0 To m_byte_length+ 2- 1 Do
    (*$r-*)
    l_result:= l_result+ Format('%1x ', [m_bytes[l_index]]);
    (*$r+*)
   display(' '+ l_result);
   End;
 End; // widesstring_Click
 
et le r駸ultat:
02_widestring_delphi_6
 
 Les librairie d'importation des API Windows (comme WINDOWS.PAS) comportaient en g駭駻al 3 versions pour chaque proc馘ure ou fonction 
Dans cet exemple, comme pChar et pAnsiChar sont des alias, la version par d馭aut est la m麥e que la version Ansi.
 
Finalement, les types String et WideString sont compatibles en affectation, mais l'affectation d'une WideString ? une String peut provoquer une perte d'information. 
 
 En r駸um?, nous constatons donc 
Donc
 
 
Nous avons tout d'abord cr鳬 une toute petite unit? qui nous permet d'analyser le contenu m駑oire et disque:
   Const k_new_line= #13#10;
  Type t_byte_array= Array[0..0] Of Byte;
     t_new_string_header=
       Packed Record
        m_code_page: Word;
        m_element_size: Word;
        m_reference_count: Longint;
        m_element_count: Longint;
        m_bytes: t_byte_array;
       End;
     t_pt_new_string_header= ^t_new_string_header;
 
  Function f_byte_array_to_hex(Var pv_byte_array: t_byte_array;
         p_count: Integer): String;
  Function f_display_unicode_string(Var pv_unicode_string: String): String;
  Function f_display_ansi_string(Var pv_ansi_string: AnsiString): String;
  Function f_stream_hex_dump(p_c_stream: tStream): String;
  Function f_file_hex_dump(p_file_name: String): String;
  Implementation
   Uses SysUtils
     ;
 
  Function f_byte_array_to_hex(Var pv_byte_array: t_byte_array; p_count: Integer): String;
   Var l_index: integer;
    Begin
    Result:= '';
    For l_index := 0 To p_count- 1 Do
     (*$r-*)
     Result:= Result + Format('%1x ', [pv_byte_array[l_index]]);
     (*$r+*)
    End; // f_byte_array_to_hex
 
  Function f_display_unicode_string(Var pv_unicode_string: String): String;
   Begin
    With t_pt_new_string_header(Integer(pv_unicode_string)- 12)^ Do
    Begin
     Result:= ' string :  >'+ pv_unicode_string+ '< Length: '
         + IntToStr(Length(pv_unicode_string))+ k_new_line
       + Format(' code page  %d', [m_code_page])+ k_new_line
       + Format(' element size %d', [m_element_size]) + k_new_line
       + Format(' ref_count  %d', [m_reference_count]) + k_new_line
       + Format(' byte count  %d', [m_element_count]) + k_new_line
       + ' bytes $   '+ f_byte_array_to_hex(m_bytes,
         m_element_count* m_element_size)
    End; // with t_pt_new_string_header^
   End; // f_display_unicode_string
 
  Function f_display_ansi_string(Var pv_ansi_string: AnsiString): String;
   Begin
    With t_pt_new_string_header(Integer(pv_ansi_string)- 12)^ Do
    Begin
     Result:= ' string :  >'+ pv_ansi_string+ '<Length: '
         + IntToStr(Length(pv_ansi_string))+ k_new_line
       + Format(' code page  %d', [m_code_page])+ k_new_line
       + Format(' element size %d', [m_element_size]) + k_new_line
       + Format(' ref_count  %d', [m_reference_count]) + k_new_line
       + Format(' byte count  %d', [m_element_count]) + k_new_line
       + ' bytes $   '+ f_byte_array_to_hex(m_bytes,
         m_element_count* m_element_size)
    End; // with t_pt_new_string_header^
   End; // f_display_ansi_string
 
  Function f_stream_hex_dump(p_c_stream: tStream): String;
   Var l_index: integer;
     l_byte: Byte;
    Begin
    Result:= '';
    p_c_stream.Position:= 0;
    For l_index:= 0 To p_c_stream.Size- 1 Do
    Begin
     p_c_stream.Read(l_byte, 1);
     Result:= Result + Format('%1x ', [l_byte]);
    End; // for l_index
   End; // f_byte_array_to_hex
 
  Function f_file_hex_dump(p_file_name: String): String;
   Var l_c_file_stream: tFileStream;
   Begin
    l_c_file_stream:= tFileStream.Create(p_file_name, fmOpenRead);
    Result:= f_stream_hex_dump(l_c_file_stream);
    l_c_file_stream.Free;
   End; // f_file_hex_dump
 
End. //
 
 Et voici un programme qui manipule des String (donc UnicodeString) 
Procedure do_display(p_string: String);
 Var l_index: Integer;
  Begin
  With Form1 Do
   Begin
   display('[len='+ IntToStr(Length(p_string))+ '] '+ p_string);
   ListBox1.Items.Add(p_string);
   RichEdit1.Text:= RichEdit1.Text+ p_string;
   Memo2.Lines.Add(p_string);
   Edit1.Text:= Edit1.Text+ p_string;
 
   display(p_string+ ' '+ f_display_unicode_string(p_string));
   For l_index:= 1 To Length(p_string) Do
    display(Format('%2d ', [l_index])+ p_string[l_index]);
  End;
  End; // do_display
 
 10_display_unicode_string
 
 
 
Y figure, par exemple
U+1D6C0 MATHEMATICAL BOLD CAPITAL OMEGA
qui est, semble-t-il une sorte d'alias de
U+03A9 GREEK CAPITAL LETTER OMEGA
Pour afficher ce symbole, nous pouvons utiliser plusieurs techniques:
et
 // 1D6C0 MATHEMATICAL BOLD CAPITAL OMEGA	
 do_display('omega ConvertFromUtf32 ', ConvertFromUtf32(1ドルD6C0));
 
 do_display('omega #USC4 ', #1ドルD6C0);
 
 do_display('surrogate ## ', #$D835#$DEC0);
 
 l_string:= #$D835#$DEC0;
 do_display('surrogate, via string, # ', l_string);
 
 SetLength(l_string, 2);
  l_string[1]:= #$D835;
 l_string[2]:= #$DEC0;
 do_display('surrogate, via string[n], ', l_string);
 
 // -- the NON SURROGATE omega
 do_display('omega, no surrogate, ', #3ドルA9);
 
Et
11_surrogate_omega
En fait:
Le m麥e .EXE a 騁? envoy? sur Windows 7 qui est sur le PC que nous avions du acheter fin 2009 pour g駻er notre site internet, et voici le r駸ultat :
surrogage_omega_windows_7.png
et:
qui retournera bien 1 pour notre surrogate omega.
 
 Pour le fun, voici l'affichage des surrogates "CJK" pr駸ent駸 plus haut : 
 surrogage_cjk_windows_7.png
 
 
 
Voici un projet qui affiche la version composite et la version non composite de quelques caract鑽es :
Nous constatons que :
 
Voici un exemple l'affichage de "?" et "?" en caract鑽e compos? ou s駱ar?: 
Procedure TForm1.display_composite_Click(Sender: TObject);
 Begin
  g_string:= '|鴃|'+ '|'+ #0065ドル+ #0301ドル+ #0069ドル+#0302ドル+ '|';
  do_display(g_string);
  End; // display_composite_Click
 
39_composite_xp
composite_windows_7
 
 
En gros, le type se comporte comme le WideChar sous les versions ant駻ieures ? Delphi 2009.
Un Char est donc toujours une valeur ordinale, qui peut 黎re utilis馥 dans For, Inc, Dec, High(Char) etc
 
 Pour cr馥r un caract鑽e ? partir de son code unit, nous pouvons utiliser : 
  l_caractere:= Chr(1234ドル);
 l_caractere:= #1234ドル;
  l_caractere:= Char(1234ドル);
 
 
En revanche nous ne pouvons PLUS utiliser un Char dans des ensembles (Set Of), puisque la valeur d'un caract鑽e est cod馥 sur 2 octets, et d駱asse donc 255 qui est la limite Pascal pour un 駘駑ent d'un Set Of. Nous devrons
modifier les instructions utilisant IN. 
 
 
Pour m駑oire, UCS4String est d馭ini comme un Array of UCS4Char (Length retourne donc le nombre de caract鑽es Usc4Char).
 
 
Le type AnsiString
   display(p_ansi_string+ ' '+ f_display_ansi_string(p_ansi_string));
   For l_index:= 1 To Length(p_ansi_string) Do
    display(Format('%2d ', [l_index])+ p_ansi_string[l_index]);
  End; // with Form1
  End; // do_display_ansi_string
 
Var g_ansi_string: AnsiString;
 
Procedure TForm1.ansistring_Click(Sender: TObject);
 Begin
   g_ansi_string:= 'abc 鴃';
  do_display_ansi_string(g_ansi_string);
  End; // ansistring_Click
 
 31_ansistring
 
 
 Nous pouvons imposer un autre code page, en le sp馗ifiant comme param鑼re de AnsiString: 
Procedure TForm1.generate_cyrillic_Click(Sender: TObject);
 Var l_cyrillic_string: t_cyrillic_string;
   l_index: Integer;
    l_string: String;
 Begin
   l_cyrillic_string:= '';
  For l_index := 127+ 65 To 127+ 65+ 7 Do
   l_cyrillic_string := l_cyrillic_string + t_cyrillic_string(AnsiChar(l_index));
   ListBox1.Items.Add(l_cyrillic_string);
  RichEdit1.Text:= RichEdit1.Text+ l_cyrillic_string;
  Memo2.Lines.Add(l_cyrillic_string);
  Edit1.Text:= Edit1.Text+ l_cyrillic_string;
  Memo1.Lines.Add(l_cyrillic_string);
 
  display('');
  display(f_display_ansi_string(AnsiString(l_cyrillic_string)));
  For l_index:= 1 To Length(l_cyrillic_string) Do
   display(
     Format('%2d %1x ', [l_index, Ord(l_cyrillic_string[l_index])])
     + l_cyrillic_string[l_index]);
 End; // generate_cyrillic_Click
 
 32_ansi_cyrillic
 
qui fournit:
33_ansi_cyrillic_hex
 
 Et si nous souhaitons afficher ou g駻er la cha?ne cyrillique, il aurait 騁? plus simple de la convertire explicitement par une affectation ? une String : 
qui fournit:
34_ansi_cyrillic_convert
Ici, nous avons construit la cha?ne en utilisant un string AnsiString ayant le code page cyrillique, puis l'avons convertie en UnicodeString pour analyse et affichage
 
Notez que les conversions entre code page peuvent provoquer des pertes d'information. Voici un exemple o? nous convertissons entre le code page 437 et 1252: 
Procedure TForm1.convert_code_page_Click(Sender: TObject);
 Var l_save_default_system_codepage: Word;
   l_ansi_ibm: t_ansi_ibm_oem;
   l_ansi_string: AnsiString;
  Begin
  display('current default Ansi code page is '+ IntToStr(DefaultSystemCodePage));
  display('');
   l_save_default_system_codepage:= DefaultSystemCodePage;
  DefaultSystemCodePage:= 1252;
 
   l_ansi_ibm:= #$C6;
  display('ansi_ibm $C6 :'+ l_ansi_ibm+ Format(' code_page %4d Ord $%2x ',
    [ StringCodePage(l_ansi_ibm), Ord(l_ansi_ibm[1])]));
 
  l_ansi_string:= l_ansi_ibm;
  display('ansi := ibm :'+ l_ansi_string+ Format(' code_page %4d Ord $%2x ',
    [ StringCodePage(l_ansi_string), Ord(l_ansi_string[1])]));
 
  l_ansi_ibm:= l_ansi_string;
  display('ibm := ansi :'+ l_ansi_ibm+ Format(' code_page %4d Ord $%2x ',
    [ StringCodePage(l_ansi_ibm), Ord(l_ansi_ibm[1])]));
 
  DefaultSystemCodePage:= l_save_default_system_codepage;
 End; // ansistring_Click
 
et voici le r駸ultat, qui d駑ontre que 437 -> 1252 -> 427 provoque une perte d'information:
35_convert_code_pages
 
 Si nous avions utilis? une cha?ne UnicodeString, cela ne serait pas arriv?: 
   l_ansi_ibm:= #$C6;
  display('ansi_ibm $C6 :'+ l_ansi_ibm + Format(' code_page %4d Ord $%2x ',
    [StringCodePage(l_ansi_ibm), Ord(l_ansi_ibm[1])]));
 
  l_unicode_string:= l_ansi_ibm;
  display('uni:= ibm  :'+ l_unicode_string+ Format(' code_page %4d Ord $%2x ',
    [ StringCodePage(l_ansi_ibm), f_utf_16_at(l_unicode_string, 1)]));
   l_ansi_ibm:= l_unicode_string;
  display('ansi := uni :'+ l_ansi_ibm + Format(' code_page %4d Ord $%2x ',
    [StringCodePage(l_ansi_ibm), Ord(l_ansi_ibm[1])]));
 End; // convert_unicode_ansi_Click
 
et voici le r駸ultat, qui d駑ontre que 437 -> 1252 -> 427 provoque une perte d'information:
36_convert_code_pages_unicode
 
 
Ce type a la propri騁? de prendre le code page de la cha?ne Ansi qui lui est affect?:
   l_raw_byte_string:= #$C6;
  display('raw:= #$C6   :'+ l_raw_byte_string + Format(' code_page %4d Ord $%2x ',
    [StringCodePage(l_raw_byte_string), Ord(l_raw_byte_string[1])]));
 
  l_ansi_ibm := #$C6;
  display('ansi_ibm:= $C6 :'+ l_ansi_ibm + Format(' code_page %4d Ord $%2x ',
    [StringCodePage(l_ansi_ibm), Ord(l_ansi_ibm[1])]));
 
  l_raw_byte_string:= l_ansi_ibm;
  display('raw:= ibm   :'+ l_raw_byte_string + Format(' code_page %4d Ord $%2x ',
    [StringCodePage(l_raw_byte_string), Ord(l_raw_byte_string[1])]));
 
  l_ansi_string := #$C6;
  display('ansi := $C6  :'+ l_ansi_string + Format(' code_page %4d Ord $%2x ',
    [StringCodePage(l_ansi_string), Ord(l_ansi_string[1])]));
 
  l_raw_byte_string:= l_ansi_string;
  display('raw:= ansi   :'+ l_raw_byte_string + Format(' code_page %4d Ord $%2x ',
    [StringCodePage(l_raw_byte_string), Ord(l_raw_byte_string[1])]));
 End; // raw_byte_string_Click
 
et voici le r駸ultat, qui d駑ontre que 437 -> 1252 -> 427 provoque une perte d'information:
38_convert_code_pages_raw_byte
 
 
 
Une nouvelle unit? ANSISTRINGS.PAS contient la plupart des proc馘ures et fonctions que nous utilisions jadis pour manipuler les cha?nes Ansi, comportant les noms utilis駸 avant 2009, ainsi qu'une version sp馗ifiant le pr馭ixe "ansi" explicitement: 
Function AnsiUpperCase(Const S: AnsiString): AnsiString; overload;
Function AnsiCompareStr(Const S1, S2: AnsiString): Integer; Inline; overload;
 
Notez que SYSUTILS.PAS comportait aussi des fonctions Ansi_xxx, comme AnsiUpperCase:
toutefois
 
 
 
 
 
 
 
 
 
 
Cette unit? offre deux versions
Var ma_chaine: String;
 ma_chaine:= tCharacter.ConvertFromUtf32(#1ドルD6C0);
 
 Var ma_chaine: String;
 ma_chaine:= ConvertFromUtf32(#1ドルD6C0);
 
Function GetNumericValue(C: Char): Double; overload; Inline
Function IsControl(C: Char): Boolean; overload; Inline;
Function IsDigit(C: Char): Boolean; overload; Inline;
Function IsLetter(C: Char): Boolean; overload; Inline;
Function IsLetter(Const S: String; Index: Integer): Boolean; overload; Inline;
Function IsLetterOrDigit(C: Char): Boolean; overload; Inline;
Function IsLetterOrDigit(Const S: String; Index: Integer): Boolean; overload; Inline;
Function IsLower(C: Char): Boolean; overload; Inline;
Function IsLowSurrogate(C: Char): Boolean; overload; Inline;
Function IsNumber(C: Char): Boolean; overload; Inline;
Function IsPunctuation(C: Char): Boolean; overload; Inline;
Function IsSeparator(C: Char): Boolean; overload; Inline;
Function IsSymbol(C: Char): Boolean; overload; Inline;
Function IsWhiteSpace(C: Char): Boolean; overload; Inline;
Function IsUpper(C: Char): Boolean; overload; Inline;
Function IsSurrogate(Surrogate: Char): Boolean; overload; Inline;
Function IsHighSurrogate(C: Char): Boolean; overload; Inline;
 
 
 
Nous pouvons rencontrer des probl鑪es partout o?, par exemple
Nous sommes donc forc駸 d'utiliser Unicode.
Si votre code n'est pas en porte ? faux par rapport aux sp馗ificit駸 Unicode, tout fonctionnera comme avant. C'est, d'apr鑚 notre exp駻ience, le cas pour 70 % des applications. Dans les autre cas, il faudra
 
 En revanche, notre code pouvait utiliser, pour acc駘駻er certains traitements, de primitives qui travaillaient fondamentalement sur des caract鑽es de 1 octet.
En r饌lit?, ces primitives nous viennent de l'Apple ][. Pour fournir un traitement de texte pleine page sur un ordinateur avec 30 K de m駑oire, une horloge ? 4 KHz et un langage interpr騁er, pour initialiser le tampon du
traitement de texte, le Pascal UCSD avait introduit la primitive FillChar : 
depuis une adresse m駑oire (param鑼re Var sans type) nous remplissions une zone de taille donn馥 avec un octet. De fa輟n similaires, nous avions Move pour d駱lacer des octets plus rapidement que ne l'aurait fait une boucle For.
Nous pouvions utiliser ces primitives sous Delphi 6:
ou encore, pour extraire une cha?ne ne contenant que des lettres minuscules (Delphi 6) :
 l_start_index:= l_index;
 While (l_index<= Length(g_text)) And (g_text[l_index] In ['a'..'z']) Do
  Inc(l_Index);
 l_copy_count:= l_index+ 1- l_start_index;
 SetLength(l_result, l_copy_count);
 If l_copy_count> 0
  Then Move(g_text[l_start_index], l_result[1], l_copy_count);
 
 
 Comme les param鑼res "nombre" de FillChar et Move sont des octets, si nous faisons fonctionner ce code sous Delphi 2009 
Et pour cr馥r une cha?ne ayant une longueur donn馥, c'est SetLength qu'il faut utiliser (depuis longtemps d駛?), plut?t que GetMem qui ne g鑽e naturellement pas le prologue des String.
 
 En r駸um?, seul l'emploi de FillChar ou Move devront 黎re examin駸 de pr鑚
lorsque ces primitives sont utilis駸 sur des String. 
 
 
Si les caract鑽es que nous testons ont un code inf駻ieur ? 128 (ou que leur code unit est la m麥e que le code de notre code page), le test fonctionnera. 'A', ou '?' seront test駸 correctement. En revanche le test de l'euro ne sera pas correct, car son code unit est 20ドルAC, alors que sont code ansi est 80ドル:
 Procedure check(p_text: String; p_char: Char; p_ansi_char: AnsiChar; p_set_of_ansi_char: t_set_of_ansi_char);
  Var l_result: String;
   Begin
   If p_char In p_set_of_ansi_char
    Then l_result:= ' ok'
    Else l_result:= ' no';
 
   l_result:= Format('%-15s $%2x  %4x ', [p_text, Word(p_char), Byte(p_ansi_Char)])+ l_result;
   display(l_result);
   End; // check
 
 Begin // ansistring_Click
  check('A in [''A'', ''B'']', 'A', 'A', ['A', 'B']);
  check('? in [''?'']', '?', '?', ['?']);
  check('? in [''?'']', '?', '?', ['?']);
 End; // ansistring_Click
 
fournira:
56_set_of_char
 Plusieures solution 
Function CharInSet(C: AnsiChar; Const CharSet: TSysCharSet): Boolean; 
  overload; Inline;
Function CharInSet(C: WideChar; Const CharSet: TSysCharSet): Boolean; 
  overload; Inline;
 
L'aide sp馗ifie que nous ne pouvons pas utiliser tSysCharSet pour cr馥r un ensemble de caract鑽es Unicode. Et pour cause, la version Unicode convertit le caract鑽e dans sa version Ansi, et compare cette valeur ? l'ensemble Set of AnsiChar que vous avez pass? en param鑼re
Voici un exemple:
 Procedure check(p_text: String; p_result: Boolean);
  Var l_result: String;
   Begin
   If p_result
    Then l_result:= ' OK'
    Else l_result:= ' NO';
   display(Format('%-30s ', [p_text])+ l_result);
  End; // check
 
 Var l_char: Char;
   l_ansi_char: AnsiChar;
   l_string: String;
   l_ansi_string: AnsiString;
 
  Begin // char_in_set_Click
  check('CharInSet(''?'', [''?''])', CharInSet('?', ['?']));
  check('l_char:= ''?; ''CharInSet(l_char, [''?''])', CharInSet(l_char, ['?']));
   l_ansi_char:= AnsiChar(l_char);
  check('l_ansi_char:= AnsiChar(l_char); ''CharInSet(l_ansi_char, [''?''])',
    CharInSet(l_ansi_char, ['?']));
 
  l_string:= '?';
   l_ansi_string:= l_string;
  check('l_string:= ''?''; l_ansi_string:= l_string; ''CharInSet(l_ansi_string[1], [''?''])',
    CharInSet(l_ansi_string[1], ['?']));
 End; // generate_cyrillic_Click
 
dont voici le r駸ultat :
57_char_in_set
Notez que:
 
Nous avons d駛? vu que les API Windows traduites en Delphi ont 騁? adapt馥s en Unicode (les versions sans "W" sont Unicode, et il existe encore les versions "A" au besoin)
 
 
  t_compute my_compute;
  t_compute * my_pt_compute; 
  my_compute= calloc ( ...) ;
  my_pt_compute = & my_compute ;
 my_pt_compute = my_pt_compute + 3;
 
 
A cause de cette facilit? ? d駸igner n'importe quelle emplacement m駑oire en se d駱la軋nt par "+", certains logiciels utilisent beaucoup les pChar. Or, actuellement, il s'agit de pointeurs de WideChar. Et donc toute l'arithm騁ique
est fauss馥. 
La solution est 騅idente:
Dans notre exemple, l'incr駑entation de 1 augmente l'adresse du pointeur de la taille de la cellule, ici 4 octets.
Pour les nostalgiques de l'arithm騁ique des pChar, l'unit? SYSTEM.PAS d馭init
Nous pr馭駻ons n饌nmoins utiliser des pointeurs vers des types bien d馭inis, et, au besoin, utiliser des calculs d'adresse, comme nous l'avons fait pour analyser le contenu m駑oire dans les exemples ci-dessus.
 
 
Delphi propose plusieurs primitives pour 馗rire ou lire cette signature
 
 Tout d'abord, les fonctions tStrings.SaveToFile et tStrings.LoadFromFile on un param鑼re optionnel de type tEncoding. 
tEncoding
   With TStringList.Create Do
  Begin
    Text:= 'A'+ #0045ドル+ #0301ドル+ 'Z';
 
   If l_encoding= 'ASCII'
    Then SaveToFile(l_save_file_name, TEncoding.ASCII) Else
   If l_encoding= 'UTF-8'
    Then SaveToFile(l_save_file_name, TEncoding.UTF8) Else
   If l_encoding= 'UTF-16 LE'
    Then SaveToFile(l_save_file_name, TEncoding.Unicode) Else
   If l_encoding= 'UTF-16 BE'
    Then SaveToFile(l_save_file_name, TEncoding.BigEndianUnicode);
   display(Format('%-10s ', [l_encoding])+ f_file_hex_dump(l_save_file_name));
    Free;
   End; // with TStringList
 End; // save_to_file_Click
 
et voici le r駸ultat :
50_tencoding
Les nouvelles classes tStreamWrite, tStreamReader ont aussi un param鑼re tEncoding.
 
 Notez que : 
La simple affectation, ou un appel de proc馘ure permet donc de provoquer la conversion.
Rappelons que
C'est un peu choquant, car le surtypage consiste normalement uniquement ? demander au Compilateur de consid駻er une zone m駑oire avec le format que nous sp馗ifions par le surtypage, et non le type de la d馗laration.
Voici un exemple ou nous surtypons un Integer par un tableau de 4 octets:
Procedure TForm1.surtypage_Click(Sender: TObject);
 Var l_integer: Integer;
   l_pt_byte_array: t_pt_byte_array;
   l_result: String;
    l_index: Integer;
 Begin
   l_integer:= 11223344ドル;
  display('@addr $'+ IntToHex(Integer(@l_integer), 4)+ ' value $'+ IntToHex(l_integer, 4));
   l_pt_byte_array:= t_pt_byte_array(@l_integer);
  l_result:= '@addr $'+ IntToHex(Integer(l_pt_byte_array), 4)+ ' value $';
  For l_index:= 0 To 3 Do
   l_result:= l_result+ ' '+ IntToHex(l_pt_byte_array^[l_index], 2);
  display(l_result);
  End; // surtypage_Click
 
qui fournit le r駸ultat suivant:
52_casting
donc, nous avons bien demand? au compilateur de consid駻er les 4 octets de l'entier comme un tableau de 4 octets (m麥e adresse, m麥e contenu, mais analys? avec un autre format)
 
 
  With t_pt_new_string_header(Integer(l_ansi_string)- 12)^ Do
   display(IntToStr(m_code_page));
  With t_pt_new_string_header(Integer(UnicodeString(l_ansi_string))- 12)^ Do
   display(IntToStr(m_code_page));
 End; // ansistring_Click
 
qui fournit :
53_string_casting
et qui d駑onter que notre AinsiString a bien 騁? transform馥 en une autre structure, tant au point de vue taille que prologue (le code page).
 
 En revanche, le surtypage par pChar et pAnsiChar se comportent comme un
"surtypage pur", sans conversion. Donc 
  l_pv_string:= 'pv '+ l_string;
  display('param_v '+ String(l_pv_string));
  pv_pchar:= pChar(l_pv_string);
 
  l_result_string:= 'resu '+ l_string;
  Result:= pChar(l_result_string);
 End; // f_test_pchar
 
Procedure TForm1.pchar_Click(Sender: TObject);
 Var l_string: String;
   l_pchar, l_pv_pchar, l_result_pchar: pChar;
   l_result_string: String;
  Begin
  l_string:= 'abc';
   // OK l_result_pchar:= f_test_pchar(@l_string[1], l_pv_pchar);
  l_result_pchar:= f_test_pchar(pChar(l_string), l_pv_pchar);
  display(String(l_result_pchar)+ ' param_v '+ String(l_pv_pchar)+ '<');
  display(String(f_test_pchar(pChar('abc'), l_pv_pchar))
    + ' param_v '+ String(l_pv_pchar)+ '<');
 End; // pchar_Click
 
dont voici le r駸ultat :
55_pchar_casting
Et voici un exemple similaire avec des pAnsiChar:
  l_pv_string:= 'pv '+ l_string;
  display('param_v '+ String(l_pv_string));
  // NO 1stChar pv_p_ansi_char:= pAnsiChar(l_pv_string);
  l_pv_ansi_string:= 'pv '+ l_string;
  pv_p_ansi_char:= pAnsiChar(l_pv_ansi_string);
 
  l_result_string:= 'resu '+ l_string;
  // NO 1st char Result:= pAnsiChar(l_result_string);
  l_result_ansi_string:= 'resu '+ l_string;
  Result:= pAnsiChar(l_result_ansi_string);
 End; // f_test_p_ansi_char
 
Procedure TForm1.p_ansi_char_call_Click(Sender: TObject);
 Var l_string: String;
   l_p_ansi_char, l_pv_p_ansi_char, l_result_p_ansi_char: pAnsiChar;
   l_result_string: String;
  Begin
  l_result_p_ansi_char:= f_test_p_ansi_char('abc', l_pv_p_ansi_char);
  display(String(l_result_p_ansi_char)+ ' param_v '+ String(l_pv_p_ansi_char)+ '<');
  display(String(f_test_p_ansi_char('abc', l_pv_p_ansi_char))+ ' param_v '+ String(l_pv_p_ansi_char)+ '<');
 End; // p_ansi_char_call_Click
 
qui fournit :
56_pansichar_casting
 
 Par cons駲uent, nous devrons donc v駻ifier si le projet ? modifier contient des surtypages, et qu'ils sont corrects en unicode. 
 
 
Il faut 騅iter :
Les String, avant ou apr鑚 Delphi 2009, sont des structures Delphi, et n馗essitent un gestionnaire de m駑oire sp馗ifique. Celui ci peut 黎re import? dans le .DLL en important, comme premi鑽e unit? de la Library, de .DPR et de l'unit? qui utilise la DLL l'unit? SHAREMEM.
Voici une .DLL que nous allons compiler avec Delphi 6:
 Function f_d6_echo(p_string_z: pChar): pChar; Stdcall;
  Var l_string: String;
   Begin
   l_string:= p_string_z;
   l_string:= IntToStr(Length(l_string))+ ' echo '+ l_string;
   l_string:= l_string+ ' '+ IntToStr(Length(l_string));
   Result:= pChar(l_string);
  End; // f_echo_d6
 
 Function f_d6_string_echo(p_string: String): String; Stdcall;
  Var l_string: String;
   Begin
   l_string:= IntToStr(Length(p_string))+ ' echo '+ p_string;
   Result:= l_string+ ' '+ IntToStr(Length(l_string));
  End; // f_d6_string_echo
 
 Exports f_d6_echo, f_d6_string_echo;
 
End
 
Appeler la .DLL Delphi 6 depuis Delphi 2010 ne pose aucun probl鑪e pour la fonction utilisant des pChar: 
Procedure TForm1.static_import_pchar_Click(Sender: TObject);
 Var l_ansi_sring: AnsiString;
   l_string, l_result: String;
   l_p_ansichar, l_pt_result: pAnsiChar;
  Begin
  display(f_d6_echo('abc'));
 
  l_ansi_sring:= 'abc';
   // NO incompatible l_p_ansichar:= pAnsiString(l_ansi_sring);
  l_p_ansichar:= @l_ansi_sring[1];
  l_pt_result:= f_d6_echo(l_p_ansichar);
  l_result:= String(l_pt_result);
  display(l_result);
  End; // static_import_Click
 
 
 En revanche, utiliser la fonction avec des param鑼res, des param鑼res variable ou des r駸ultat de fonction String ne semble pas possible 
 
 En r駸um? 
Il est possible d'utiliser des utilitaires qui d騁ectent les zones ? modifier et en pr駸ente la list. Pour cela, il suffit d'analyser le source et recherchent
Nous utilisons un analyseur d駻iv? de notre analyseur lexical Delphi (non publi?) ? qui nous avons ajout駸 des couches logiques pour d騁ecter les probl鑪es potentiels
 
 
A ce niveau, il est recommand? de brancher tous les avertissements. Nous modifions ces avertissements par "projet | options | compilateur | avertissements":
58_unicode_warnings
Tir? de l'aide, voici Les warnings concernant les cha?nes de caract鑽es:
Toutefois
Il faudra, de toutes les fa輟ns g駻er en Ansi
 
 
La notation utilis馥 est la notation alsacienne qui consiste ? pr馭ixer les identificateurs par la zone de compilation: K_onstant, T_ype, G_lobal, L_ocal, P_arametre, F_unction, C_lasse. Elle est pr駸ent馥 plus en d騁ail dans l'article La Notation Alsacienne
 
 Comme d'habitude: