Carnet Wiki

utf8 vrac de notes à deboguer

utf8 vrac de notes à deboguer

Interviennent les paramètres suivant, dont il faut s’assurer qu’ils restent toujours en phase :

Pour les données :
-  le codage déclaré pour la table dans la base de donnée
-  le codage déclaré pour la base de donnée (est-ce utile vraiment, si on connait le codage de la table ?)
-  le codage du transfert, lorsqu’il y a transfert. (c’est quoi cette notion au juste ?)
-  le codage réel du CONTENU de la table, qui en cas d’erreur à une étape peut être différent de celui théorique (indiqué par le codage de la table)

Pour le source :
-  le codage du fichier (ISO, utf8 avec ou sans bom...). Pour SPIP (càd les .html ?) il faut le BOM, mais il ne le faut pas pour les fichiers php. A noter que php n’élimine pas le bom lors d’un include().
-  la manière avec laquelle l’éditeur de texte comprend l’encodage du fichier.

Gildas C : Le dénominateur commun des projets / fichiers libres multilingues... : Il
faut encoder en UTF-8. C’est la norme aussi pour les fichiers de langue donc pas besoin de s’embêter avec les entités numériques ou nommées...
et sans BOM bien sûr (ça perturbe PHP par exemple et c’est difficile à déceler)
Si le système ou l’éditeur de code n’est pas compatible Unicode (ou fait de
l’Unicode mais encodé autrement), il faut utiliser ISO Latin-1 qui est entièrement compatible.


Erreurs possibles

-  déclarer quelquepart utf-8 alors qu’il faut écrire UTF-8 ou UTF8 ou utf8
-  l’inverse et toutes les variations possibles

À php on dit mb_internal_encoding("UTF-8");
mais à MySql on dit SET NAMES 'utf8'

-  des données ou du code en utf8 dans un contenant déclaré en ISO
-  des données ou du code en ISO dans un contenant déclaré en uf8

-  un contenant en utf8 lu ou analysé par un outil qui croit que c’est de l’ISO
-  un contenant en ISO lu ou analysé par un outil qui croit que c’est de l’utf8

Par ailleurs, les fichiers déclarés comme utf8 peuvent avoir un BOM ou ne pas en avoir.

Cela introduit une autre source d’erreur :
-  un fichier créé sans BOM ou analysé par un outil qui croit qu’il y a un BOM ou qui a besoin qu’il y en ait un, alors que le fichier n’en a pas.
-  l’inverse et toutes les variations possibles.


Outils de dépatouillage avancé

  1. Le témoignage de Fil : http://zzz.rezo.net/Reparer-le-charset-d-une-base-SPIP.html pour corriger un site où des données utf8 ont été enregistrées dans des tables ISO (= latin1)
  1. Faire connaissance avec des caractères accenctués (ou d’autres caractères spéciaux ?) et la manière symptomatique avec laquelle ils se présentent en cas d’erreur.
    C’est à dire : apprendre à lire les hiéroglyphes.
    Se documenter sur comment l’ISO et l’utf8 codent les caractères accenctués et autres caractères spéciaux.

Données :
-  regarder le contenant : codage déclaré pour une table
-  regarder le contenu (comment ? avec quel outil et comment régler cet outil pour qu’il n’introduise pas de biais supplémentaires ? ) : y a t il des caractères bizares ? les caractères bizarres ressemblent ils à des hiéroglyphes connus, comme de l’utf8 regardé dans une page ISO ou le contraire ?

Code :
-  idem

Code fourni sur IRC (par cédric)

Migration de charsets dans une base SPIP vers UTF8

Code (from cerdic)

  1. /**
  2.  * Migrer le charset de chaque champ en ne machouillant pas le contenu
  3.  * @param string $serveur
  4.  */
  5. function monplugin_migre_charset_champs($serveur=''){
  6. $res = sql_showbase('%',$serveur);
  7. while($row = sql_fetch($res,$serveur)){
  8. $table = reset($row);
  9. #var_dump($table);
  10.  
  11. $desc = sql_showtable($table,false,$serveur);
  12. #var_dump($desc);
  13.  
  14. foreach($desc['field'] as $champ=>$d){
  15. if (strpos($d,'CHARACTER SET latin1')!==false){
  16. //ALTER TABLE `spip_articles` CHANGE `soustitre` `soustitre` TEXT CHARACTER SET BINARY NOT NULL
  17. //ALTER TABLE `spip_articles` CHANGE `soustitre` `soustitre` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL
  18.  
  19. $d_binary = str_ireplace('CHARACTER SET latin1','CHARACTER SET BINARY',$d);
  20. $d_utf8 = str_ireplace('CHARACTER SET latin1','CHARACTER SET utf8 COLLATE utf8_general_ci',$d);
  21.  
  22. // on passe le champ en binary
  23. sql_alter("TABLE $table CHANGE $champ $champ $d_binary",$serveur);
  24. // puis en utf8
  25. sql_alter("TABLE $table CHANGE $champ $champ $d_utf8",$serveur);
  26.  
  27. #var_dump($champ);
  28. #var_dump($d_utf8);
  29. #die();
  30. }
  31. if (time()>_TIME_OUT) return;
  32. }
  33.  
  34.  
  35. if (time()>_TIME_OUT) return;
  36. }
  37.  
  38. // mettre a jour la meta de connexion via sql_xx car ecrire_meta ne gere pas la base distante
  39. $r = array('nom' => 'charset_sql_connexion', 'valeur' => 'utf8');
  40. $desc = sql_showtable("spip_meta",false,$serveur);
  41. sql_insertq('spip_meta', $r, $desc, $serveur);
  42. sql_updateq('spip_meta', $r,"nom=" . sql_quote($r['nom']),$desc,$serveur);
  43.  
  44. }

Télécharger

Discussion sur IRC

  1. <cerdic> Suske: tadam
  2. http://spip.pastebin.fr/36883
  3. migration d'une base mysql iso en utf
  4. in situ
  5. <Suske> (oh)
  6. <cerdic> sans aucun export de données et robuste
  7.  
  8. <cerdic> en fait mysqlfait des conversions de charset quand on passe de binary a utf8
  9. il suffit de faire un double alter sur chaque champ
  10. (mais fait un backup avant quand même pour verifier)
  11.  
  12. <Suske> oué je suis en train d'injecter le backup là
  13. ça prend déjà un peu de temps ^^
  14. merci merci
  15. <cerdic> si jamais le contenu est encodé en UTF8 mais la base est en iso il faut passer par binary
  16. <Suske> je crois que j'ai de quoi jouer entre chaque sieste de la journée
  17. <cerdic> si le contenu est iso dans une base iso et qu'il faut juste passer en utf8
  18. il faut pas passer par binary
  19. <Suske> non le contenu est iso (et le site est encore en 2.1)
  20. <cerdic> donc voila
  21. <Suske> je me disais que j'allais juste tenter le convert de spip2.1
  22. <cerdic> en general je joue une premiere fois pour voir si c'est bon
  23. <Suske> enfin il y a un peu de pourriture dans le charset comme même...
  24. <cerdic> et en fonction du resultat j'adapte les alter

Télécharger


Fonctions PHP

Voir http://php.net/manual/fr/ref.mbstring.php

-  quelles fonctions de chaine classiques fonctionnent bien avec l’utf8 ?

SPIP a créé une fonction spip_strlen indépendante du charset, qui appelle strlen en ISO, sinon mb_strlen si elle existe, ou sinon l’émule avec strlen(preg_replace(',[\x80-\xBF],S', '', $c));.
Cela pourrait indiquer que preg_replace fonctionne bien avec l’utf8.
Dans quel cas peut on utiliser des fonctions ’classiques’ et quand faut il utiliser les mb_ ?

-  quelles fonctions exigent par contre une fonction mb_truc ?

On peut supposer que toutes les fonctions qui ont une contrepartie mb_truc ne sont pas compatibles avec l’utf8.

-  faut il traduire preg_replace en mb_ereg_replace ? Apparament non vu que spip_strlen fait appel à preg_replace sur de l’utf8 (mais dans un cas où la chaine à remplacer ne contient pas d’utf8. Peut être est-ce nécessaire dans le cas contraire...)


Recommandations pour de bonnes pratiques

-  Regarder en particulier la fonction mb_internal_encoding(« UTF-8 ») ; http://php.net/manual/fr/function.mb-internal-encoding.php

-  Aussi : mb_http_output http://php.net/manual/fr/function.mb-http-output.php


les caractères

-  voir http://www.lookuptables.com/
-  http://en.wikipedia.org/wiki/Non-breaking_space
-  http://en.wikipedia.org/wiki/Space_(punctuation)
-  http://en.wikipedia.org/wiki/Non-breaking_space

-  \n == &#13;

-  &#8201; également appelée U+2009 est ici directement compris entre les pipes : | | c’est une espace fine (à comparer à une espace normale ici : | | ; dans ce contexte on voit pas beaucoup la différence. Accessible aussi par &thinsp en HTML comme ici : | | . Et pour finir une espace « sixième de m » : | | ah on voit mieux. Mais les tailles peuvent dépendre des polices du rendu, qui définissent, ou non, ces espaces )
<!ENTITY thinsp  CDATA "&#8201;" -- thin space, U+2009 ISOpub -->

-  0xA0 est un non breaking space en ISO, mais c’est U+00A0 en unicode, ou 0xC2 0xA0 en UTF8.