Carnet Wiki

Bouée de sauvetage utf8

Lecture

-  https://contrib.spip.net/Convertir-un-site-SPIP-3-en-utf-8-avec-le-plugin
-  https://contrib.spip.net/Passez-votre-base-SPIP-en-Unicode
-  https://contrib.spip.net/Comment-passer-son-site-en-utf-8
-  http://zzz.rezo.net/Reparer-le-charset-d-une-base-SPIP.html
-  https://blog.sodifrance.fr/encodage-et-migration-de-la-base-de-donnees-de-spip-en-utf-8
 : Stéphane S. indique avoir utilisé les indications de cette page avec succès, depuis windows et vers un mutu OVH, moyennant les petites adaptations suivantes :

> mysqldump --host=exampletest.mysql.db --user=exampletest --password=example --quote-names --set-charset --default-character-set=latin1 exampletest > dump1.sql
> cat dump1.sql |sed -e 's/latin1/utf8/g' > dump2.sql
> mysql --host=exampletest.mysql.db --user=exampletest --password=example exampletest < dump2.sql

Contexte

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

Pour les données :
-  le codage d’interclassement /utilisé par MySQL pour les tris de colonnes/

  • celui déclaré pour la base de données (est-ce utile vraiment, si on connait le codage de chaque table ?)
  • le codage d’interclassement déclaré pour chaque table dans la base de données
    -  le codage du fichier texte de transfert, lorsqu’il y a export ou import Adminer/PhpMyAdmin, (c’est quoi cette notion au juste ? l’encodage du fichier !)
    -  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)

Dans les données SPIP
-  la valeur de la méta charset : Configuration / Langue / Jeu de caractères
c’est normalement à ? utf8 ? utf-8 !
-  les valeurs de charset_sql_base et charset_sql_connexion sont normalement à utf8
-  la valeur charset_collation_sql_base reprend l’interclassement utf8_general_ci

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.

Sources d’erreurs


-  déclarer quelque part 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.

En conséquences vous pouvez perdre des affichages, ou pire, tronquer vos données dans certains champs...

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 bizzares ? les caractères bizarres ressemblent ils à des hiéroglyphes connus, comme de l’utf8 regardé dans une page ISO ou le contraire ?

Code :
-  idem

Migration de charsets dans une base SPIP vers UTF8

Cerdic :

Sans aucun export de données et robuste. En fait mysql fait des conversions de charset quand on passe de binary a utf8. Il suffit de faire un double alter sur chaque champ (mais faites un backup avant quand même pour verifier).
-  si jamais le contenu est encodé en UTF8 mais la base est en iso il faut passer par binary
-  si le contenu est iso dans une base iso et qu’il faut juste passer en utf8, il faut pas passer par binary

En general je joue une premiere fois pour voir si c’est bon, et en fonction du resultat j’adapte les alter.

/**
 * Migrer le charset de chaque champ en ne machouillant pas le contenu
 * @param string $serveur
 */
function monplugin_migre_charset_champs($serveur=''){
	$res = sql_showbase('%',$serveur);
	while($row = sql_fetch($res,$serveur)){
		$table = reset($row);
		#var_dump($table);

		$desc = sql_showtable($table,false,$serveur);
		#var_dump($desc);

		foreach($desc['field'] as $champ=>$d){
			if (strpos($d,'CHARACTER SET latin1')!==false){
				//ALTER TABLE <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+c3BpcF9hcnRpY2xlczwvY29kZT4="></span> CHANGE <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+c291c3RpdHJlPC9jb2RlPg=="></span> <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+c291c3RpdHJlPC9jb2RlPg=="></span> TEXT CHARACTER SET BINARY NOT NULL
				//ALTER TABLE <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+c3BpcF9hcnRpY2xlczwvY29kZT4="></span> CHANGE <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+c291c3RpdHJlPC9jb2RlPg=="></span> <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+c291c3RpdHJlPC9jb2RlPg=="></span> TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL

				$d_binary = str_ireplace('CHARACTER SET latin1','CHARACTER SET BINARY',$d);
				$d_utf8 = str_ireplace('CHARACTER SET latin1','CHARACTER SET utf8 COLLATE utf8_general_ci',$d);

				// on passe le champ en binary
				sql_alter("TABLE $table CHANGE $champ $champ $d_binary",$serveur);
				// puis en utf8
				sql_alter("TABLE $table CHANGE $champ $champ $d_utf8",$serveur);

				#var_dump($champ);
				#var_dump($d_utf8);
				#die();
			}
			if (time()>_TIME_OUT) return;
		}


		if (time()>_TIME_OUT) return;
	}

	// mettre a jour la meta de connexion via sql_xx car ecrire_meta ne gere pas la base distante
	$r = array('nom' => 'charset_sql_connexion', 'valeur' => 'utf8');
	$desc = sql_showtable("spip_meta",false,$serveur);
	sql_insertq('spip_meta', $r, $desc, $serveur);
	sql_updateq('spip_meta', $r,"nom=" . sql_quote($r['nom']),$desc,$serveur);

}

Convertir toutes les tables de latin1 à utf98

C’est une application du double CONVERT évoqué plus haut par cerdic, appliqué sur toutes les tables. Code MYSQL by Stéphane Santon :

SET NAMES utf8;

ALTER TABLE www18_articles CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_articles CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_articles CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE www18_auteurs CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_auteurs CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_auteurs CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE www18_breves CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_breves CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_breves CONVERT TO CHARACTER SET utf8;
ALTER TABLE www18_documents CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_documents CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_documents CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_forum CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_forum CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_forum CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_groupes_mots CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_groupes_mots CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_groupes_mots CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_messages CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_messages CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_messages CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_meta CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_meta CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_meta CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_mots CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_mots CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_mots CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_ortho_cache CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_ortho_cache CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_ortho_cache CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_ortho_dico CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_ortho_dico CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_ortho_dico CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_petitions CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_petitions CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_petitions CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_referers CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_referers CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_referers CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_referers_articles CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_referers_articles CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_referers_articles CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE www18_rubriques CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_rubriques CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_rubriques CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_signatures CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_signatures CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_signatures CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_syndic CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_syndic CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_syndic CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_syndic_articles CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_syndic_articles CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_syndic_articles CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_tickets CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_tickets CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_tickets CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_types_documents CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_types_documents CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_types_documents CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_versions CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_versions CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_versions CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_versions_fragments CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_versions_fragments CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_versions_fragments CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE www18_visites CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_visites CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_visites CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_visites_articles CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_visites_articles CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_visites_articles CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE www18_zones CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_zones CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_zones CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci;
ALTER TABLE www18_zones_liens CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
ALTER TABLE www18_zones_liens CONVERT TO CHARACTER SET binary;
ALTER TABLE www18_zones_liens CONVERT TO CHARACTER SET utf8  COLLATE utf8_general_ci; 

Fonctions PHP utiles

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

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

  • str_replace ?
  • preg_replace ?
  • trim ?
  • strlen ?
  • strpos ?
  • etc ?

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...)

Note : il y a un mode u dans preg_replace pour l’utf-8.
-  voir : https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
-  et : https://www.php.net/manual/fr/regexp.reference.unicode.php

-  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.

JLuc - Mise à jour :17 février 2021 à 20h14min