OVH Cloud OVH Cloud

Détecter UTF8

14 réponses
Avatar
Gloops
Bonjour tout le monde,

J'ai bien un jeu de fonctions pour convertir UTF8 en ANSI et
inversement, mais maintenant la question serait plutôt de savoir SI le
contenu d'une chaîne de caractères est en UTF8, pour savoir si il doit
être converti (si on fait la conversion à tort on perd les caractères
accentués).

Je ne me fais pas trop d'illusions, mais peut-être que quelqu'un sait
faire ...

J'ai fait un récapitulatif d'une boîte mail d'Eudora, ça se passe bien
sauf sur ce point. Comme la boîte contient des messages en UTF8 (envoyés
depuis un site webmail) et des messages en ISO8859, il s'agit de faire
le tri. J'ai bien regardé au début du message, dans le code HTML, et je
n'y ai pas trouvé ce que je cherchais. Pas à la fin, quand même ?

Si ça se trouve il y a un manque au niveau du stockage, comme Eudora
assurera bientôt le support UTF8 ça résoudra la question, mais si jamais
quelqu'un a une idée géniale ça serait dommage de s'en priver.


A tout hasard je remets le code de la conversion, puisque je trouve
beaucoup plus facilement les messages des gens qui demandent comment on
fait, que la réponse. De ce fait j'ai oublié qui me l'a donné, désolé.

Conversion UTF-8 / ANSI
' =======================================
Option Explicit

Private Declare Function MultiByteToWideChar Lib "Kernel32" _
(ByVal CodePage As Long, ByVal dwFlags As Long, _
ByVal lpMultiByteStr As Long, ByVal cchMultiByte As Long, _
ByVal lpWideCharStr As Long, ByVal cchWideChar As Long) As Long

Private Declare Function WideCharToMultiByte Lib "Kernel32" _
(ByVal CodePage As Long, ByVal dwFlags As Long, _
ByVal lpWideCharStr As Long, ByVal cchWideChar As Long, _
ByVal lpMultiByteStr As Long, ByVal cchMultiByte As Long, _
ByVal lpDefaultChar As Long, ByVal lpUsedDefaultChar As Long) _
As Long

Private Const CP_ACP = 0
Private Const CP_UTF8 = 65001
' =======================================
Public Function UTF8_Encode(ByVal Text As String) As String

Dim sBuffer As String
Dim lLength As Long

lLength = WideCharToMultiByte(CP_UTF8, 0, StrPtr(Text), -1, 0, 0, 0, 0)
sBuffer = Space$(lLength)
lLength = WideCharToMultiByte(CP_UTF8, 0, _
StrPtr(Text), -1, StrPtr(sBuffer), Len(sBuffer), 0, 0)
sBuffer = StrConv(sBuffer, vbUnicode)
UTF8_Encode = Left$(sBuffer, lLength - 1)

End Function
' =======================================
Public Function UTF8_Decode(ByVal Text As String) As String

Dim lLength As Long
Dim sBuffer As String

Text = StrConv(Text, vbFromUnicode)
lLength = MultiByteToWideChar(CP_UTF8, 0, StrPtr(Text), -1, 0, 0)
sBuffer = Space$(lLength)
lLength = MultiByteToWideChar(CP_UTF8, 0, _
StrPtr(Text), -1, StrPtr(sBuffer), Len(sBuffer))
UTF8_Decode = Left$(sBuffer, lLength - 1)

End Function

4 réponses

1 2
Avatar
Fred
Dans : news:43ab324d$0$19701$
Gloops écrit :
Fred a écrit, le 22/12/2005 21:41 :

C'est le contraire, tu peux conclure que ce n'est pas de l'UTF-8 dans
certains cas, mais tu ne peux jamais conclure que cela en est ;-)



Même si tu vois
"cheminots en grève" ?



Je parlais du flux d'octets, analysé par programme.
Si tu vois cela, alors c'est de l'UTF-8. L'algorithme te l'a dit (avec
une probabilité très très forte) mais tu l'as quand même affiché
autrement qu'en UTF-8.

Je t'accorde que la probabilité d'arriver au bout de tes octets en
ayant validé toutes les règles précédentes avec autre chose que de
l'UTF-8 est proche de zéro.



Peut-être bien.



Tu viens d'en donner un exemple ;-)
Trouver cette succession de caractères «grèves» dans un texte /normal/
est extrêmement faible !
Bon, je chipote. Ma remarque était uniquement destinée à souligner un
point de logique.
Le non respect des règles que j'ai citées implique que ce n'est pas de
l'UTF-8. Mais leur respect ne permet de conclure qu'avec une
quasi-certitude.

Autre exemple :
Postulat : Si la fenêtre est ouverte, alors il y a quelqu'un dans la
pièce.
Question : La fenêtre est fermée, Y a-t-il quelqu'un dans la pièce ?
Oui - Non - Ne sais pas.
;-)



--
Fred
http://www.cerbermail.com/?3kA6ftaCvT
Avatar
Fred
Dans : news:43ab3108$0$6676$
Gloops écrit :
Salut,



Aussi j'ai regardé le contenu de ma boîte d'entrée à l'aide d'un
éditeur de texte, là j'ai vu que la plupart des messages ont un ou
des champs Content-type.



Oui, c'est à cela que je faisai allusion dans mon premier post.
Je pensais que tu connaissais le format brut des mails.

Logiquement, on peut s'attendre à ce que
ceux qui n'en ont pas soient tous du même type, ou alors ça va être
dur pour mes cheveux ...



Et non. Avec OE sous Windows par exemple, si les en-têtes MIME sont
absents (ce qui est le cas dans le paramétrage par défaut pour les
*news*, c'est le charset windows-1252 qui est utilisé. Avec un autre
logiciel et sous un autre système, cela peut-être un autre charset. Mais
ce n'est pas très correct qu'un message n'utilise pas MIME. On n'est
plus à l'époque de l'ASCII tout de même.

Peut-être y a-t-il une faiblesse de l'objet d'accès à la boîte, si
cette propriété n'est pas accessible individuellement avec. Je verrai
cela dans le newsgroup Eudora, des fois que j'aie mal regardé, ce
serait
quan même plus simple. Toujours est-il qu'il y a une propriété
RawMessage qui retourne tout le texte reçu au titre du message,
champs inclus. Ce n'est pas la solution la plus élégante mais en
faisant une recherche là-dedans avec InStr on devrait s'en sortir. Si
c'est ça qu'il faut faire, ça promet quand même d'être du sport pour
les multipart ...




Cela me semble au contraire la bonne méthode. Vérifier dans le raw
l'encodage utilisé par l'émetteur et lui faire confiance pour
l'affichage. Si pas d'encodage, mettre en ouvre la reconnaisance
automatique de l'UTF-8 au cas où, bien que ne crois pas qu'un message
encodé en UTF-8 (assez récent) ne précise pas son content-type. Sinon il
restera encore à déterminer si c'est un charset windows-1252,
ISO-8859-1, ISO-8859-15, etc ...
Ces derniers charsets ayant la particularité commune d'encoder un
caractère avec un seul octet.

Je ne connais pas la finalité de ton programme, mais si c'est pour
afficher les messages, il y a aussi la possibilité de laisser à
l'utilisateur le soin de choisir son encodage de visualisation quand la
reconnaissance échoue (voir menu affichage de IE ou OE par exemple)

Ce que je ne comprends pas, c'est comment tu récupérais tes messages
avant d'utiliser le RawMessage ? Je suppose que tu utilisais une autre
méthode et que celle-ci se chargeait de transformer l'encodage du
message en Unicode (utilisé par les chaînes VB), à l'exception de
l'utf-8 ?

Tout cela n'est pas très clair :-)
Dis-toi qu'entre ce qui circule sur le NET (un flux d'octets) et le
stockage dans une chaîne VB, il y a déjà beaucoup de travail à faire.
octets ----> codes propres à un encodage ----> conversion en
unicode ----> chaîne VB

Première étape :
en utf-8, chaque octet ou série d'octets correspond à une valeur de mot
qui est le code d'un caractère dans le jeu Unicode.
Pour les encodages ISO, chaque octet correpond à un caractère dans le
jeu normalement spécifié (peu de travail)
Mais ! Certains messages, pour ne pas contenir d'octets supérieurs à 7F,
peuvent mettre en ouvre des techniques telles que Quoted-Printable ou
Base64. Dans le cas du Quoted-Printable, un code de caractère supérieur
à 7F est encodé sous la forme =HH (où HH est le code de caractère en
hexa). Je passe sur les détails (voir les RFC). En base 64, c'est tout
le message (tableau d'octets) qui est encodé ... en base 64.

À la fin de cette première étape, on récupère donc une série de valeurs
qui sont des codes de caractères (sur un ou deux octets) *dans un cerain
charset*

Deuxième étape :
À l'aide de tables de correspondances, convertir ces valeurs en valeurs
Unicode.
Pour l'utf-8, pas de conversion à ce niveau.
Pour le reste, prenons l'exemple de l'euro dont le code caractère est
différent si l'on se place en windows-1252 ou en ISO-8859-15 (en
ISO-8859-1, il n'existe pas) (au passage attention, en UTF-8, l'euro se
retrouve codé sur trois octets)

Troisième étape :
Les valeurs Unicode sont stockées en mémoire dans la chaîne VB (mais là
j'ai un petit doute avec VB6)
Quand on les affiche, on ne fait que dessiner les caractères
corespondants à chaque /mot/ à l'écran.

Je pense que pour mieux comprendre ce problème, il faut voir un message
non pas comme un texte mais comme une succession d'octets. Et là, on
perçoit tout de suite mieux la difficulté.

--
Fred
http://www.cerbermail.com/?3kA6ftaCvT
Avatar
Gloops
Fred a écrit, le 23/12/2005 08:19 :
Même si tu vois
"cheminots en grève" ?




Je parlais du flux d'octets, analysé par programme.
Si tu vois cela, alors c'est de l'UTF-8. L'algorithme te l'a dit (avec
une probabilité très très forte) mais tu l'as quand même affiché
autrement qu'en UTF-8.



C'était plus explicite à lire en mettant toute l'expression (d'ailleurs
si j'avais mis boulangers à la place de cheminots tu n'aurais rien
compris), mais n'empêche que si dans le flux on trouve
chr$(195) + chr$(168)
il y a de bonnes chances qu'on ait affaire à de l'UTF8.

Parce que dans une conversation courante, on n'a pas souvent l'occasion
de placer un A majuscule tilde, suivi d'un tréma isolé. Je me demande
bien comment ça se prononce, d'ailleurs.


Je t'accorde que la probabilité d'arriver au bout de tes octets en
ayant validé toutes les règles précédentes avec autre chose que de
l'UTF-8 est proche de zéro.




Peut-être bien.




Tu viens d'en donner un exemple ;-)



Ah oui il faudrait que je relise les règles pour bien comprendre, mais
il semble que nous sommes sur la même longueur d'ondes.

Trouver cette succession de caractères «grèves» dans un texte /normal/
est extrêmement faible !
Bon, je chipote. Ma remarque était uniquement destinée à souligner un
point de logique.
Le non respect des règles que j'ai citées implique que ce n'est pas de
l'UTF-8. Mais leur respect ne permet de conclure qu'avec une
quasi-certitude.



De toute manière, si on n'a pas de hiéroglyphes, à la limite peu importe
qu'on décode en UTF-8 ou en ISO8859, le résultat sera proche.


Autre exemple :
Postulat : Si la fenêtre est ouverte, alors il y a quelqu'un dans la pièce.
Question : La fenêtre est fermée, Y a-t-il quelqu'un dans la pièce ? Oui
- Non - Ne sais pas.
;-)



Boh oui mais il est mort : ça fait tellement longtemps qu'elle est fermée.
Bon là je digresse pas mal ...
Avatar
Gloops
Fred a écrit, le 23/12/2005 08:56 :

Dans : news:43ab3108$0$6676$
Gloops écrit :

Salut,




Aussi j'ai regardé le contenu de ma boîte d'entrée à l'aide d'un
éditeur de texte, là j'ai vu que la plupart des messages ont un ou
des champs Content-type.




Oui, c'est à cela que je faisai allusion dans mon premier post.
Je pensais que tu connaissais le format brut des mails.



J'avais mal regardé. L'objet d'accès au message (EuMessage) a quatre
propriétés avec le texte
- Body interprété selon le contexte
- BodyAsHTML
- BodyAsSimpleText
- RawMessage contient ausi les champs d'entête, celui-là m'avait échappé
dans une première approche


Logiquement, on peut s'attendre à ce que
ceux qui n'en ont pas soient tous du même type, ou alors ça va être
dur pour mes cheveux ...




Et non. Avec OE sous Windows par exemple, si les en-têtes MIME sont
absents (ce qui est le cas dans le paramétrage par défaut pour les
*news*, c'est le charset windows-1252 qui est utilisé. Avec un autre
logiciel et sous un autre système, cela peut-être un autre charset. Mais
ce n'est pas très correct qu'un message n'utilise pas MIME. On n'est
plus à l'époque de l'ASCII tout de même.



Chaque logiciel retient un jeu de caractères par défaut ?
Bon, avec ça on devrait s'en sortir.
Si on veut fignoler, on donne à l'utilisateur le choix du jeu de
caractères par défaut, pour les messages ne comportant pas de précision.


Peut-être y a-t-il une faiblesse de l'objet d'accès à la boîte, si
cette propriété n'est pas accessible individuellement avec. Je verrai
cela dans le newsgroup Eudora, des fois que j'aie mal regardé, ce serait
quan même plus simple. Toujours est-il qu'il y a une propriété
RawMessage qui retourne tout le texte reçu au titre du message,
champs inclus. Ce n'est pas la solution la plus élégante mais en
faisant une recherche là-dedans avec InStr on devrait s'en sortir. Si
c'est ça qu'il faut faire, ça promet quand même d'être du sport pour
les multipart ...





Cela me semble au contraire la bonne méthode. Vérifier dans le raw
l'encodage utilisé par l'émetteur et lui faire confiance pour
l'affichage. Si pas d'encodage, mettre en ouvre la reconnaisance
automatique de l'UTF-8 au cas où, bien que ne crois pas qu'un message
encodé en UTF-8 (assez récent) ne précise pas son content-type. Sinon il
restera encore à déterminer si c'est un charset windows-1252,
ISO-8859-1, ISO-8859-15, etc ...
Ces derniers charsets ayant la particularité commune d'encoder un
caractère avec un seul octet.



La joyeuseté, c'est qu'il faut fractionner entre les différentes
parties, et trouver l'encodage pour chacune. Si c'est différent de l'une
à l'autre il faudra traiter différemment en allant chercher les parties
dans le RawMessage ... Ou décider de n'en traiter qu'une, si il s'agit
d'un message reçu avec une partie texte pur et une partie HTML.

Il faut avouer que ça sera plus long et moins élégant que si on avait eu
un Message.Parts(1).Code = "UTF-8", Message.Parts(2).Code = "ISO8859-1"

Bon enfin ça, ça dépend d'une autre boutique ...


Je ne connais pas la finalité de ton programme, mais si c'est pour
afficher les messages, il y a aussi la possibilité de laisser à
l'utilisateur le soin de choisir son encodage de visualisation quand la
reconnaissance échoue (voir menu affichage de IE ou OE par exemple)



J'avais bien pensé à ça, mais en fait il s'agit d'un récapitulatif de
boîte aux lettres, or la correspondante a utilisé alternativement UTF8
et ISO8859, donc on a les deux à afficher sur la même page. Donc elle
aurait pu afficher alternativement selon l'un puis l'autre standard,
mais pour avoir tout le texte correct d'un coup, de cette manière c'est
rapé ...


Ce que je ne comprends pas, c'est comment tu récupérais tes messages
avant d'utiliser le RawMessage ? Je suppose que tu utilisais une autre
méthode et que celle-ci se chargeait de transformer l'encodage du
message en Unicode (utilisé par les chaînes VB), à l'exception de l'utf-8 ?



Voir en tête ci-dessus


Je pense que pour mieux comprendre ce problème, il faut voir un message
non pas comme un texte mais comme une succession d'octets. Et là, on
perçoit tout de suite mieux la difficulté.



J'avais oublié la propriété RawMessage, avec ça je peux faire le
traitement moi-même là où je ne trouve pas ce que je cherche dans les
propriétés toutes prêtes.

ça devrait commencer à tenir la route.
Merci.
1 2