Salut.
Après un peu d'histoire sur Unicode et utf-8 dans ce billet, nous avons vu dans ce billet la théorie du codage d'un caractère Unicode en utf-8. Il est temps maintenant de passer à la pratique, et d'écrire deux fonctions en VBA pour coder une chaine de caractères en utf-8, mais également de convertir une chaine reçue en utf-8 en chaine lisible sur notre machine.
En clair, nous allons voir comment passer de Je voudrais un café s'il vous plait. Ça vous fera 4€, Monsieur à Je voudrais un café s'il vous plait. Ça vous fera 4€, Monsieur et vice-versa. Pour réaliser cela, on va manipuler les bits avec des ET, des OU et des décalages.
Il faut bien comprendre ici que lorsqu'on transforme de l'unicode en utf-8, on transforme une chaine de caractère en une valeur binaire (des octets). Lorsque l'on transforme de l'utf-8 en unicode, on transforme une valeur binaire (des octets) en une chaine de caractères
Sachez aussi que certaines bibliothèques permettent de réaliser ces transformations (notamment ADODB avec Stream). Mais il n'existe pas à ma connaissance de fonction native VBA pour réaliser cela. D'où l'utilité d'avoir ces fonctions sous le coude, ou plutôt dans le module Tools à embarquer dans tous vos développements VBA. On notera cependant une limitation des fonctions proposées ci-dessous: Le VBA ne permet pas de récupérer les unicodes au delà de 65535. Dans les faits, ce n'est normalement pas limitant, sauf bien sûr à devoir utiliser des plans unicode qui vont au delà du code 65535.
Mais d'abord, voici les fonctions de conversion
Code vba : | Sélectionner tout |
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 | Function Uni2Utf(Text As String) As String Dim v As Long Dim i As Long For i = 1 To Len(Text) v = AscW(Mid(Text, i, 1)) Select Case v Case Is < 128 Uni2Utf = Uni2Utf & Mid(Text, i, 1) Case Is < 2048 Uni2Utf = Uni2Utf & Chr(((v And 1984) / 64) Or 192) Uni2Utf = Uni2Utf & Chr((v And 63) Or 128) Case Else Uni2Utf = Uni2Utf & Chr(((v And 61440) / 4096) Or 224) Uni2Utf = Uni2Utf & Chr(((v And 4032) / 64) Or 128) Uni2Utf = Uni2Utf & Chr((v And 63) Or 128) End Select Next End Function Function Utf2Uni(Text As String) As String Dim v As Long Dim i As Long: i = 1 Do While i <= Len(Text) v = Asc(Mid(Text, i, 1)) Select Case v Case Is < 128 Utf2Uni = Utf2Uni & Mid(Text, i, 1) i = i + 1 Case Is < 224 Utf2Uni = Utf2Uni & ChrW((v And 63) * 64 + (Asc(Mid(Text, i + 1, 1)) And 63)) i = i + 2 Case Else Utf2Uni = Utf2Uni & ChrW(((v And 31) * 4096) + _ ((Asc(Mid(Text, i + 1, 1)) And 63) * 64) + _ (Asc(Mid(Text, i + 2, 1)) And 63)) i = i + 3 End Select Loop End Function |
Rappels sur l'arithmétique binaire et booléenne
Lorsque l'on décale les bits de n rangs vers la droite, on divise en fait la valeur exprimée par 2n. Lorsque l'on décale les bits de n rangs vers la gauche, on multiplie la valeur exprimée par 2n:
- 6 / 2 = 3 => 0110 / 10 = 0011;
- 24 / 8 = 3 => 11000 / 1000 = 0011;
- 5 * 2 = 10 => 0101 * 10 = 1010;
- 7 * 4 = 28 => 0111 * 100 = 11100.
Si l'on souhaite isoler certains bits d'une valeur binaire, on réalise un ET logique de cette valeur avec les bits que l'on veut isoler à 1:
01011001 => 01011001 AND 00011100 => 00011000
Si l'on souhaite allumer un bit (le passer à 1), on réalise un OU logique avec ce bit à 1:
01001111 OU 00100000 => 01101111 (Ici, cela revient à ajouter 32)
Si l'on souhaite éteindre un bit (le passer le 0), on réalise un ET logique avec ce bit à 0 et tous les autres à 1:
01101111 ET 11011111 => 01001111 (ici, cela revient à retrancher 32)
Décorticage des fonctions
Si vous avez lu le second billet cité plus haut, vous vous souviendrez de la logique de conversion et du tableau qui illustrait les cas possibles
- un seul octet => 0xxxxxxx (7 bits pour le code);
- deux octets => 110xxxxx 10xxxxxx (11 bits pour le code);
- trois octets => 1110xxxx 10xxxxxx 10xxxxxx (16 bits pour le code);
- quatre octets => 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (21bits pour le code).
On comprend grâce à ce tableau que pour passer de unicode vers utf-8, on sépare les bits de couleurs dans des octets puis on place les bits de contrôle (les bits de poids fort) qui permettent de savoir de combien d'octets est composé le caractère. Lorsque l'on passe de utf-8 vers unicode, supprime les bits de contrôle et l'on rassemble les bits de couleurs des octets qui composent le caractère en une seule valeur, la valeur unicode du caractère codé. Ces opérations sont effectuées à coup de AND, OR et de décalages (multiplications et divisions).
unicode vers utf-8
Code vba : | Sélectionner tout |
v = AscW(Mid(Text, i, 1))
Code vba : | Sélectionner tout |
1 2 | Case Is < 128 Uni2Utf = Uni2Utf & Mid(Text, i, 1) |
Code vba : | Sélectionner tout |
1 2 3 | Case Is < 2048 Uni2Utf = Uni2Utf & Chr(((v And 1984) / 64) Or 192) Uni2Utf = Uni2Utf & Chr((v And 63) Or 128) |
- v And 1984 => On isole les 5 bits de poids fort sur les 11 (1984dec = 11111000000bin)
- /64 permet de décaler les 5 bits retenus de 6 rangs vers la droite (64 = 26)
- Or 192 => +192 pour ajouter le "code" qui indique que l'on aura 2 octets pour le caractère (192dec = 11000000bin)
avec xxxxx000000bin, on obtient donc la valeur 110xxxxxbin
Le second caractère sera obtenu avec Chr((v And 63) Or 128):
- v And 63 isole les 6 derniers bits de la valeur (63dec = 00111111bin)
- Or 128 ajoute 128 (128dec = 10000000bin)
Avec yyxxxxxxbin, on obtient 10xxxxxxbin
Je pense qu'il n'est pas nécessaire de développer la suite de la fonction, vous avez compris la manoeuvre.
utf-8 vers unicode
ChrW(Valeur) permet de récupérer le caractère dont on passe le code unicode.
Code : | Sélectionner tout |
1 2 3 | Case Is < 128 Utf2Uni = Utf2Uni & Mid(Text, i, 1) i = i + 1 |
Code : | Sélectionner tout |
1 2 3 | Case Is < 224 Utf2Uni = Utf2Uni & ChrW((v And 63) * 64 + (Asc(Mid(Text, i + 1, 1)) And 63)) i = i + 2 |
- (v And 63) * 64 isole les 5 bits de poids faible et les décale de 6 rangs vers la gauche;
- (Asc(Mid(Text, i + 1, 1)) And 63) passe à l'octet suivant pour isoler les 6 bits de poids faible 10xxxxxx (Dans les faits, vue le deuxième bit de poids fort est à 0, ça revient à retrancher 128 = passe le bit de poids fort à 0 => 00xxxxxx).
110xxxxx 10xxxxxx devient donc à recomposer la valeur des 11 bits représentés par les x. Bien sûr, vu que l'on a traité 2 octets, on ajoute i à 2 pour "sauter" l'octet de poids faible que l'on vient de traiter.
Vous l'aurez compris, c'est la même logique qui prévaut pour recomposer l'unicode tenant sur 3 octets.
Bien entendu, ces fonctions sont utilisables en Excel:
Voilà. J'espère que ces trois billets sur unicode et utf-8 vous ont plu et qu'ils vous ont permis de mieux comprendre le codage utf-8 d'une chaine de caractères unicode. J'espère également que les deux fonctions données vous permettront de mieux faire dialoguer vos données VBA avec le monde extérieur...
Petit plus: Si vous le souhaitez, vous pouvez saisir du texte dans Notepad++ pour le convertir dans un sens ou dans l'autre. Par exemple, créez une nouvelle page dans Notepad++ et assurez-vous qu'elle est en codage utf-8, puis saisissez Bonjour, un café svp. Ça fera 2€, monsieur. En basculant le codage de la page en ANSI, vous obtiendrez Bonjour, un café svp. Ça fera 2€, monsieur (comme avec la fonction Uni2Utf...)
.