IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Composants VCL Delphi Discussion :

La fonction UTF8Decode de Delphi 6 PE est incomplète ou a un gros bug !


Sujet :

Composants VCL Delphi

  1. #1
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    361
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 361
    Billets dans le blog
    1
    Par défaut La fonction UTF8Decode de Delphi 6 PE est incomplète ou a un gros bug !
    Je suis sous W16 avec Delphi 6 PE.

    Je suis en train d'approfondir l'usage de l'Unicode qui n'est pas nativement supporté par Delphi 6 PE.
    Néanmoins, certaines fonctions d'aide sont disponibles, telles que UTF8Decode (dans System).

    A première vue, c'est impeccable... Mais à y regarder de plus près, il y a un gros problème.
    En effet, cette fonction est chargée de convertir un string Ansi de Delphi contenant du code UTF-8 (multi-byte) en WideString contenant de l'Unicode en format UTF-16.
    UTF-16 code la majeure partie des codepoint Unicode en un seul mot de 17 bits, mais certains sont codés sur deux mots 16 bits.

    En entrée, les caractères Unicode en UTF-8 sont codés sur 1, 2, 3 ou 4 octets. Et celle fonction UTFEncode est chargée de convertir UTF-8 en UTF-16.

    Mais, UTF8Encode est incapable de traiter des codes UTF-8 sur 4 octets !

    En effet, dans ce cas, la fonction plante tout simplement et le résultat retourné est une chaîne WideString vide.

    J'ai donc décidé d'écrire ma propre fonction UTF8EncodeEx éliminant ce défaut. Mieux: si une combinaison d'octsts représente un codepoint invalide, non défini en Unicode, ma fonction signale l'anomalie en donnant l'index de l'octet invalide, tout en retournant le début de la chaîne convertie avant l'occurrence de l'octet invalide.

    En ièces jointes à ce post, j'ai placé un fichier ZIP contenant le projet entier avec ma nouvelle fonction etlrprogramme de démo montrant le problème et sa solution.
    Voici une capture d'écran:
    Nom : aa1.png
Affichages : 141
Taille : 22,6 Ko
    Ceci représente le ésultat en utilisant le bouton "UTF8Decode_Klaus avec UTF8 4 bytes".
    La 3ème colone montre le code ITF-8 hé,éré par ma fonction. Les 4 derniers octets 31 à 34 représentent un codepoint sur 4 octets:
    31 F0
    32 90
    33 80
    34 A4
    Il est correctement traduit en UTF6 dans les deux derniers mots 11 et 12:
    11 D840
    12 DC24
    Et voici le résultat avec le bouton "UTF8Decode standard sans UTF8 4 bytes":
    Nom : aa2.PNG
Affichages : 136
Taille : 20,0 Ko

    Par contre, le bouton "UTF8Decode standard avec UTF8 4 bytes" donne le résultat suivant:
    Nom : aa3.png
Affichages : 136
Taille : 17,7 Ko

    Mon implémentation est certainement maladroite et on peut certainement mieux faire, peut-être même en utilisant l'assembleur inline pour le traitement des groupes de bits des différents octets.

    Qu'est-ce que vous pensez de ce code ?
    Fichiers attachés Fichiers attachés

  2. #2
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 041
    Par défaut
    C'est sûr que la version UTF8Decode de D6 ayant plus de 20 ans ne supporte peut-être pas toute les évolutions des API Windows dont elle dépend !

    Pourquoi ne pas étudier directement MultiByteToWideChar et WideCharToMultiByte

    Tu as donc comme avant dernier caratère

    U+0AF9
    UTF8 : E0 AB B9 soit Gujarati Letter Zha, c'est pas du chinois mais l'un des nombreux dialectes indiens

    On devine vaguement ce catactères très petit dans le TTntEdit dans les images ci-dessus, celui-ça même si illisible, il semble bien le bon

    Donc il te manque U+10024, UTF8 : F0 90 80 A4 soit Linear B Syllable B078 Qe, du Linear B ?
    UTF-16 : 0xD800 0xDC24



    Je ne vois rien qui y ressemble dans le TTntEdit dans les images ci-dessus, qui va écrire dans une langue morte de la Grèce antique ?
    Du coup, c'est quoi le dernier caractère que l'on voit, cela ressemble à du chinois comme si la fonction alternative avait calculé une autre lettre

    A mon avis, c'est U+20024 qui est affiché donc F0 A0 80 A4 ... ça c'est UTF16 : D840 DC24
    CJK UNIFIED IDEOGRAPH-20024 - 𠀤

    on voit qu'il y a une erreur dans ton masque sur le 2ème octet, je te laisse corriger ta fonction pour éliminer le 0040 en trop dans le premier word du UTF16



    Sinon, UTF8 sur 4 octets, je ne vois pas à quel moment tu vas tombé sur ce cas, c'est pas genre des dialectes d'Asie et d'Afrique, ainsi que quelques langes "mortes" (antique)
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  3. #3
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    361
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 361
    Billets dans le blog
    1
    Par défaut
    Merci d'avoir bien voulu regarder mon code.

    Je suis tombé sur ce type de problème en tentant d'afficher du texte récupéré sur internet. Pour l'enregistrer sur disque, je l'avais converti en UTF8. Or, la reconversion en UTF-16 'WideString) , dans certains cas, ne produisait qu'un champ vide en l'affichant dans un TTntMemo.

    En allant pas à pas, j'ai identifié une séquence UTF-8 dont la pésence "bloque" la conversion, et en absence de cette séquence, l'affichage était correct.
    Or, il s'agissait d'un code point en 4 octets. J'ai donc fait plusieurs tests avec des code points de ce type, et le résultat était toujours le même.

    J'ai voulu trouver une solution par mes propres moyens et j'ai cherché à comprendre le mécanisme de conversion. Résultat: ma fonction UTF8DecodeEx. Mais c'est évidemment sans aucune garantie de bon foncionnement. Pour les tests, j'ai pris une ligne d'un texte en chinois (avec des caractères UTF-8 en 2 et 3 octets), et j'ai ajouté un code point en 4 octets, évidemment sans aucun lien avec la langue chinoise. Et j'ai mis au point ma fonction de cette manière.

    Maintenant, quelle utilité ? Le plaisir d'avoir compris le principe de transcodage et de pouvoir afficher n'importe quel texte. Et en gardant UTF-8 comme codage de base, je peux tout enregistrer sur disque dans un format facile à traiter, sans avior à utiliser des outils tiers - c'est du simple texte ANSI (d'ailleurs, une variable de type STRING suffit en D6, pas besoin d'un type du genre UTF8String). Mais ça, c'est un détail.

    Donc, réellement, c'était une question de fiabilité de l'outil. Tout texte Unicode doit pouvoir y passer.
    Je vais tester MultiByteToWideChar. Si le resultat est positif, je vais oublier ma fonction et adopter celle-ci.

    Encore une fois, Merci d'avoir bien voulu regarder mon code !

  4. #4
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 041
    Par défaut
    Sur ta fonction, tu n'es pas loin

    Tu as confondu $3 masque 0011 de 11110uvv soit un masque de 0111 qui donne 7 sur le byte 0
    Et tu as oublié de retrancher le Unicode code point soit $10000 d'où le $10024 et $20024

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    c := ((pb^ and $7) shl 18) or ((pb1^ and $3F) shl 12) or ((pb2^ and $3F) shl 6) or (pb3^ and $3F);
    c := c  - $10000;
    c1 := $D800 or ((c shr 10) and $3FF);
    c2 := $DC00 or (c and $3FF);
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  5. #5
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 041
    Par défaut
    Sans Delphi, donc le code est surement pas terrible mais essaye aussi ça

    Pas de copie dans un tableau intermédiaire puis qu'une AnsiString c'est juste un array of AnsiChar que l'on peut donc assimilé à un array of Byte
    C'est un algorithme unifié sur le calcul du CodePoint caractère par caractère, la gestion du nombre de char est géré par le dépassement du CodePoint $10000, le CodePoint est géré comme un registre accumulateur

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
     
    function UTF8ToWideString(const S: AnsiString): WideString;
    var
      P: PByte;
      C: Byte;
      CodePoint: Cardinal;
      K: Integer;
    begin
      Result := '';
      if S = '' then
        Exit;
     
      SetLength(Result, Length(S));
      K := 0;
     
      P := PByte(@S[1]);
      CodePoint := 0;
     
      while P^ <> 0 do
      begin
        C := P^;
     
        if C <= $7F then
          CodePoint := C
        else if C <= $BF then
          CodePoint := (CodePoint shl 6) or (C and $3F)
        else if C <= $DF then
          CodePoint := C and $1F
        else if C <= $EF then
          CodePoint := C and $0F
        else
          CodePoint := C and $07;
     
        Inc(P);
     
        if ((P^ and $C0) <> $80) and (CodePoint <= $10FFFF) then
        begin
          if CodePoint > $FFFF then
          begin
            Dec(CodePoint, $10000);
            Inc(K);
            Result[K] := WideChar($D800 or ((CodePoint shr 10) and $03FF));
            Inc(K);
            Result[K] := WideChar($DC00 or (CodePoint and $3FF));
          end
          else if (CodePoint < $D800) or (CodePoint >= $E000) then
          begin
            Inc(K);
            Result[K] := WideChar(CodePoint);
          end;
        end;
      end;
     
      SetLength(Result, K);
     
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  6. #6
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 041
    Par défaut
    J'ai lancé Delphi finalement,

    les trois fonctions retourne le même résultats sous Delphi 10

    Attention, le dernier caractère, il n'est peut-être pas imprimable car c'est un caractère grecque que la fonte de mon ancienne version Windows ne connait pas !


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    [Window Title]
    Utf8towidestring_test
     
    [Content]
    从中国来的第一封信ૹ𐀤
    从中国来的第一封信ૹ𐀤
    从中国来的第一封信ૹ𐀤
     
    [OK]
    Du coup, j'ai affiché le 20024 au lieu de 10024 soit le F0 A0 80 A4 à la place du F0 90 80 A4 ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    [Window Title]
    Utf8towidestring_test
     
    [Content]
    从中国来的第一封信ૹ𠀤
    从中国来的第一封信ૹ𠀤
    从中国来的第一封信ૹ𠀤
     
    [OK]
    Note que le copier-coller (via MSTSC) à du mal à récupérer ces caractères alternatifs donc une image

    Nom : Sans titre.png
Affichages : 93
Taille : 11,4 Ko


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    unit UTF8ToWideString_TestForm;
     
    interface
     
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
     
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        { Déclarations privées }
      public
        { Déclarations publiques }
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
     
    function UTF8ToWideString(const S: AnsiString): WideString; forward;
      function UTF8Decode_Klaus(const as8: AnsiString): WideString; forward;
     
    procedure TForm1.Button1Click(Sender: TObject);
     
      function HexaConvert(s: string): AnsiString;
      var
        s1: string;
        i, n, c: integer;
      begin
        n := Length(s);
        setLength(Result, trunc(n/3));
        for i:=1 to trunc(n/3) do begin
          s1 := Copy(s,i*3-2,2);
          c := StrToInt('$'+s1);
          result[i] := AnsiChar(c);
        end;
      end;
     
    var
      s: string;
      utf8string: AnsiString;
    begin
      s := 'E4 BB 8E E4 B8 AD E5 9B BD E6 9D A5 E7 9A 84 E7 AC AC E4 B8 80 E5 B0 81 E4 BF A1 E0 AB B9 F0 90 80 A4 ';
      utf8string := HexaConvert(s);
      ShowMessage(UTF8Decode(utf8string) + sLineBreak + UTF8ToWideString(utf8string) + sLineBreak + UTF8Decode_Klaus(utf8string));
     
      s := 'E4 BB 8E E4 B8 AD E5 9B BD E6 9D A5 E7 9A 84 E7 AC AC E4 B8 80 E5 B0 81 E4 BF A1 E0 AB B9 F0 A0 80 A4 ';
      utf8string := HexaConvert(s);
      ShowMessage(UTF8Decode(utf8string) + sLineBreak + UTF8ToWideString(utf8string) + sLineBreak + UTF8Decode_Klaus(utf8string));
    end;
     
     
     
     
    function UTF8ToWideString(const S: AnsiString): WideString;
    var
      P: PByte;
      C: Byte;
      CodePoint: Cardinal;
      K: Integer;
    begin
      Result := '';
      if S = '' then
        Exit;
     
      SetLength(Result, Length(S));
      K := 0;
     
      P := PByte(@S[1]);
      CodePoint := 0;
     
      while P^ <> 0 do
      begin
        C := P^;
     
        if C <= $7F then
          CodePoint := C
        else if C <= $BF then
          CodePoint := (CodePoint shl 6) or (C and $3F)
        else if C <= $DF then
          CodePoint := C and $1F
        else if C <= $EF then
          CodePoint := C and $0F
        else
          CodePoint := C and $07;
     
        Inc(P);
     
        if ((P^ and $C0) <> $80) and (CodePoint <= $10FFFF) then
        begin
          if CodePoint > $FFFF then
          begin
            Dec(CodePoint, $10000);
            Inc(K);
            Result[K] := WideChar($D800 or ((CodePoint shr 10) and $03FF));
            Inc(K);
            Result[K] := WideChar($DC00 or (CodePoint and $3FF));
          end
          else if (CodePoint < $D800) or (CodePoint >= $E000) then
          begin
            Inc(K);
            Result[K] := WideChar(CodePoint);
          end;
        end;
      end;
     
      SetLength(Result, K);
     
    end;
     
     
     
      function UTF8Decode_Klaus(const as8: AnsiString): WideString;
      var
        B: array of byte;
        i: integer;
        c, c1, c2, cnt: integer;
        pb, pb1, pb2, pb3: pByte;
        pw: pWideChar;
        ok: boolean;
     
        procedure IncrementBP(Increment: integer);
        begin
          inc(pb,Increment);
          inc(pb1,Increment);
          inc(pb2,Increment);
          inc(pb3,Increment);
          i := i + Increment;
        end;
     
      {
      # Bytes     First code point 	Last code point 	Byte 1      Byte 2      Byte 3      Byte 4
      1               U+0000             U+007F             0yyyzzzz
      2               U+0080             U+07FF             110xxxyy    10yyzzzz
      3               U+0800             U+FFFF             1110wwww    10xxxxyy    10yyzzzz
      4               U+010000           U+10FFFF           11110uvv    10vvwwww    10xxxxyy    10yyzzzz
      }
      begin
        SetLength(B,Length(as8));
        SetLength(result,length(as8));
        cnt := 0;
        pw := PWideChar(@result[1]);
        for i:=1 to Length(as8) do B[i-1] := Ord(as8[i]);
        pb := pByte(@B[0]);
        pb1 := pByte(@B[1]);
        pb2 := pByte(@B[2]);
        pb3 := pByte(@B[3]);
            {
              1   E4
              2   B8
              3   AD
            }
        i := 0;
        while i<Length(as8) do begin
          ok := false;
    //showmessage('A: '+inttostr(i)+': '+inttohex(integer(pb^),2));
      {
      # Bytes     First code point 	Last code point 	Byte 1      Byte 2      Byte 3      Byte 4
      1               U+0000             U+007F             0yyyzzzz
      2               U+0080             U+07FF             110xxxyy    10yyzzzz
      3               U+0800             U+FFFF             1110wwww    10xxxxyy    10yyzzzz
      4               U+010000           U+10FFFF           11110uvv    10vvwwww    10xxxxyy    10yyzzzz
      }
          if (pb^ and $80)=0 then begin
            cnt := cnt + 1;
            result[cnt] := WideChar(pb^);
            IncrementBP(1);
            ok := true;
          end else begin
    //showmessage('B: '+inttostr(i)+': '+inttohex(integer(pb^),2));
     
            if (pb^ and $E0)=$C0 then begin
    //showmessage('C: '+inttostr(i)+': '+inttohex(integer(pb^),2));
              c := ((pb^ and $1F) shl 6) or (pb1^and $3F);
              cnt := cnt + 1;
              result[cnt] := WideChar(c);
              IncrementBP(2);
              ok := true;
            end else begin
    //showmessage('D: '+inttostr(i)+': '+inttohex(integer(pb^),2)+'   '+inttohex(integer(pb^) and $E0,2));
     
              if (pb^ and $F0)=$E0 then begin
    //showmessage('E: '+inttostr(i)+': '+inttohex(integer(pb^),2));
                c := ((pb^ and $F) shl 12) or ((pb1^ and $3F) shl 6) or (pb2^ and $3F);
                cnt := cnt + 1;
                result[cnt] := WideChar(c);
                IncrementBP(3);
                ok := true;
              end else begin
    //showmessage('F: '+inttostr(i)+': '+inttohex(integer(pb^),2));
     
                if (pb^ and $F0)=$F0 then begin
    //showmessage('G: '+inttostr(i)+': '+inttohex(integer(pb^),2));
                  c := ((pb^ and $7) shl 18) or ((pb1^ and $3F) shl 12) or ((pb2^ and $3F) shl 6) or (pb3^ and $3F);
                  c := c  - $10000;
                  c1 := $D800 or ((c shr 10) and $3FF);
                  c2 := $DC00 or (c and $3FF);
                  cnt := cnt + 1;
                  result[cnt] := WideChar(c1);
                  cnt := cnt + 1;
                  result[cnt] := WideChar(c2);
                  IncrementBP(4);
                  ok := true;
                end;
     
              end;
            end;
          end;
     
          if not ok then begin
    showmessage('UTF8 error at '+inttostr(integer(pb)-integer(@B[0]))+' ('+inttohex(integer(pb1^),2)+'): '+as8);
            SetLength(result,cnt);
            exit;
          end;
        end;
        SetLength(result,cnt);
      end;
     
    end.
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  7. #7
    Membre expérimenté
    Homme Profil pro
    ‫‬
    Inscrit en
    Septembre 2024
    Messages
    156
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : ‫‬

    Informations forums :
    Inscription : Septembre 2024
    Messages : 156
    Par défaut
    Correction de la fonction ConvertWideStringToUTF32. récemment j'ai réalisé un code similaire en VBA pour retourner les code point des caractères unicodes

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
      function ConvertWideStringToUTF32(sw: widestring): TUTF32;
    var
      i, i1, n: integer;
      c, L: cardinal;
    begin
      n := Length(sw);
      SetLength(result, n);
      if n > 0 then begin
        i := 1;
        i1 := 0;
        while i<=n do begin
          c := ord(sw[i]);
          If (c >= $D800) and (c <= $DBFF) then
          begin
             if i = n then
             begin
                c := ord('?'); // invalide char ..
             end else
             begin
                 L := ord(sw[i+1]);
                 If (L >= $DC00) and (L <= $DFFF ) then
                 begin
                     c := (c and $3FF) * 1024;
                     c := (c or (L and $3FF)) + $10000;
                     inc(i);
                 end else
                      c := ord('?'); // invalide char ..
             end;
          end;
          result[i1] := c;
          i1 := i1 + 1;
          i := i + 1;
        end;
        SetLength(result,i1);
      end;
    end;
    0 00004ECE
    1 00004E2D
    2 000056FD
    3 00006765
    4 00007684
    5 00007B2C
    6 00004E00
    7 00005C01
    8 00004FE1
    9 00000AF9
    10 00020024

    A noter que la version Utf8Encode en Delphi 7 ne supporte pas les paires surrogates c'est à dire les caractères au delà de $FFFF ne sont pas codés correctement

  8. #8
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 041
    Par défaut
    Comme dans UTF8Decode_Klaus, il manquait le - $10000, c'est logique que dans ConvertWideStringToUTF32, il manquait le + $10000
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  9. #9
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    361
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 361
    Billets dans le blog
    1
    Par défaut
    J'ai rectifié mon code, et j'ai l'impression que maintenant, la conversion de TFF-8mb4 et UTF-16 et UTF-32 marche bien.
    Le programme de démo monrte un string avec 3 code points en 4 octets, séparés par un espace normal.
    Le bouton DécodeUTF8 est bien sûr en erreur - résultat vide.
    Les boutons "Decode Klaus" et "MultiByteToWideChar" donnent maintenant le même résultat:
    Nom : aa3.png
Affichages : 60
Taille : 15,2 Ko

    J'ai l'impression d'avoir compris la procédure de conversion de UTF_8mb4 --> UTF-16 --> UTF-32.

    Le projet complet est en pièce jointe.
    Fichiers attachés Fichiers attachés

  10. #10
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 041
    Par défaut
    Il te manque 10000 sur UTF32 !
    Relis nos échanges sur la correction ... tu l'as triché avec un - $40 en UTF8 mais faut mieux utiliser -/+ 10000
    C'est un nombre connu et symbolique en Unicode, c'est le début du SMP - Plan multilingue complémentaire

    Pense aussi à une autre façon de coder le UTF8 Decode : regarde UTF8ToWideString, tu verras que l'algo est plus proche de celle pour l'UTF32

    Symbole Unicode «𐌸» (U+10338)
    Symbole Unicode «😊» (U+1F60A)
    Symbole Unicode «𐐍» (U+1040D)


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
                  c1 := ($D800 or (c shr 10)) - $40;
                  c2 := $DC00 or (c and $3FF);
    question cohérence du code entre l'algo UTF8 -> UTF16 -> UTF32, ça cerait mieux d'utiliser de codes plus ressemblants
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
                  Dec(c, $10000);
                  c1 := $D800 or (c shr 10);
                  c2 := $DC00 or (c and $3FF);

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    result[i1] := (result[i1] or (c and $03FF));
    devient
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    result[i1] := (result[i1] or (c and $03FF)) + $10000;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 3
    Dernier message: 27/05/2006, 00h14
  2. Réponses: 17
    Dernier message: 14/02/2006, 00h21
  3. Comment appeler une fonction JavaScript depuis Delphi ?
    Par Alfred12 dans le forum Web & réseau
    Réponses: 4
    Dernier message: 17/06/2005, 18h15
  4. Existe-t-il une fonction Eval() sous Delphi ?
    Par Hell dans le forum Langage
    Réponses: 5
    Dernier message: 20/12/2004, 17h45
  5. Fonction pour savoir si un ordi est connecté au reseau
    Par LitteulKevin dans le forum Windows
    Réponses: 17
    Dernier message: 13/09/2004, 14h12

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo