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

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Convertir une chaine en utf-8 avec VBA
Un billet de Pierre Fauconnier

Le , par Pierre Fauconnier

0PARTAGES

Vous trouverez ici deux fonctions qui permettent de traiter les caractères utf-8 en VBA, dans les deux sens de conversion

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

  1. un seul octet => 0xxxxxxx (7 bits pour le code);
  2. deux octets => 110xxxxx 10xxxxxx (11 bits pour le code);
  3. trois octets => 1110xxxx 10xxxxxx 10xxxxxx (16 bits pour le code);
  4. 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))
AscW récupère le code unicode d'un caractère. VBA limite les valeurs à 65535 caractères, de sorte que VBA ne permettra pas de traduire les caractères dont l'unicode est supérieur à 65535. Du coup, on n'aura jamais de caractère nécessitant 4 octets. Dans les faits, ce n'est normalement pas pénalisant pour nous, sauf à devoir traduire des caractères vraiment très spécifiques.

Code vba : Sélectionner tout
1
2
      Case Is < 128 
        Uni2Utf = Uni2Utf & Mid(Text, i, 1)
Lorsque le code du caractère est <128, il est converti tel quel. On l'ajoute donc simplement à la chaine de résultat

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)
Lorsque le code est >127 et <2048 (2^11), on pourra coder le caractère sur deux octets. On prend ici 2^11 car on aura besoin de 3 bits de contrôle pour l'octet de poids fort (110xxxxx) et de deux bits de contrôle pour l'octet de poids faible (10xxxxxx). Il reste donc 11 bits sur les 16 pour coder le caractère. Le premier caractère est codé avec Chr(((v And 1984) / 64) Or 192):
  • 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
Dans le sens unicode vers utf-8, on a vu qu'un caractère dont l'unicode était inférieur à 128 était repris tel quel. Dans l'autre sens de la conversion, on va donc faire de même (l'octet "vaut" le caractère) puis incrémenter i pour passer à l'octet suivant.

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
Lorsque l'octet est supérieur à 127 mais <224, c'est que les trois bits de poids fort sont 110xxxxx. On sait donc qu'il y deux octets pour composer le caractère (110xxxxx 10xxxxxx):
  • (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...)

.

Une erreur dans cette actualité ? Signalez-nous-la !