Problème de codage UTF8 - Avec de bons exemples
J'ai le problème d'encodage de caractères suivant, d'une manière ou d'une autre, j'ai réussi à enregistrer des données avec différents encodages de caractères dans ma base de données (UTF8) Le code et les sorties ci-dessous montrent 2 exemples de chaînes et comment ils sortent. 1 d'entre eux devrait être changé en UTF8 et l'autre l'est déjà.
Comment dois-je procéder pour vérifier si je dois coder la chaîne ou non? Par exemple, j'ai besoin que chaque chaîne soit sortie correctement, alors comment vérifier si elle est déjà utf8 ou si elle doit être convertie?
J'utilise PHP 5.2, mysql myisam tables:
CREATE TABLE IF NOT EXISTS `entities` (
....
`title` varchar(255) NOT NULL
....
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
<?php
$text = $entity['Entity']['title'];
echo 'Original : ', $text."<br />";
echo 'UTF8 Encode : ', utf8_encode($text)."<br />";
echo 'UTF8 Decode : ', utf8_decode($text)."<br />";
echo 'TRANSLIT : ', iconv("ISO-8859-1", "UTF-8//TRANSLIT", $text)."<br />";
echo 'IGNORE TRANSLIT : ', iconv("ISO-8859-1", "UTF-8//IGNORE//TRANSLIT", $text)."<br />";
echo 'IGNORE : ', iconv("ISO-8859-1", "UTF-8//IGNORE", $text)."<br />";
echo 'Plain : ', iconv("ISO-8859-1", "UTF-8", $text)."<br />";
?>
Sortie 1:
Original : France Télécom
UTF8 Encode : France Télécom
UTF8 Decode : France T�l�com
TRANSLIT : France Télécom
IGNORE TRANSLIT : France Télécom
IGNORE : France Télécom
Plain : France Télécom
Sortie 2: ###
Original : Cond� Nast Publications
UTF8 Encode : Condé Nast Publications
UTF8 Decode : Cond?ast Publications
TRANSLIT : Condé Nast Publications
IGNORE TRANSLIT : Condé Nast Publications
IGNORE : Condé Nast Publications
Plain : Condé Nast Publications
Merci pour votre temps sur celui-ci. Encodage des caractères et je ne m'entends pas très bien!
MISE À JOUR:
echo strlen($string)."|".strlen(utf8_encode($string))."|";
echo (strlen($string)!==strlen(utf8_encode($string))) ? $string : utf8_encode($string);
echo "<br />";
echo strlen($string)."|".strlen(utf8_decode($string))."|";
echo (strlen($string)!==strlen(utf8_decode($string))) ? $string : utf8_decode($string);
echo "<br />";
23|24|Cond� Nast Publications
23|21|Cond� Nast Publications
16|20|France Télécom
16|14|France Télécom
Cela peut être un travail pour la fonction mb_detect_encoding()
.
D'après mon expérience limitée, il n'est pas fiable à 100% lorsqu'il est utilisé comme un "renifleur d'encodage" générique - il vérifie la présence de certains caractères et valeurs d'octets pour faire une supposition éclairée - mais dans ce cas étroit (il devra distinguer juste entre UTF-8 et ISO-8859-1) cela devrait fonctionner.
<?php
$text = $entity['Entity']['title'];
echo 'Original : ', $text."<br />";
$enc = mb_detect_encoding($text, "UTF-8,ISO-8859-1");
echo 'Detected encoding '.$enc."<br />";
echo 'Fixed result: '.iconv($enc, "UTF-8", $text)."<br />";
?>
vous pouvez obtenir des résultats incorrects pour les chaînes qui ne contiennent pas de caractères spéciaux, mais ce n'est pas un problème.
J'ai créé une fonction qui traite de tous ces problèmes. Cela s'appelle Encoding :: toUTF8 ().
<?php
$text = $entity['Entity']['title'];
echo 'Original : ', $text."<br />";
echo 'Encoding::toUTF8 : ', Encoding::toUTF8($text)."<br />";
?>
Production:
Original : France Télécom
Encoding::toUTF8 : France Télécom
Original : Cond� Nast Publications
Encoding::toUTF8 : Condé Nast Publications
Vous n'avez pas besoin de savoir quel est l'encodage de vos chaînes tant que vous savez qu'il est sur Latin1 (iso 8859-1), Windows-1252 ou UTF8. La chaîne peut également en avoir un mélange.
Encoding :: toUTF8 () convertira tout en UTF8.
Je l'ai fait parce qu'un service me donnait un flux de données tout foiré, mélangeant UTF8 et Latin1 dans la même chaîne.
Usage:
$utf8_string = Encoding::toUTF8($utf8_or_latin1_or_mixed_string);
$latin1_string = Encoding::toLatin1($utf8_or_latin1_or_mixed_string);
Télécharger:
http://dl.dropbox.com/u/186012/PHP/forceUTF8.Zip
J'ai inclus une autre fonction, Encoding :: fixUFT8 (), qui corrigera chaque chaîne UTF8 qui semble tronquée.
Usage:
$utf8_string = Encoding::fixUTF8($garbled_utf8_string);
Exemples:
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("FÃÂédÃÂération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
affichera:
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Une autre façon, peut-être plus rapide et moins fiable:
echo (strlen($str)!==strlen(utf8_decode($str)))
? $str //is multibyte, leave as is
: utf8_encode($str); //encode
Il compare la longueur de la chaîne d'origine et la chaîne utf8_decoded. Une chaîne qui contient un caractère à plusieurs octets, a un strlen qui diffère du strlen codé à un octet similaire.
Par exemple:
strlen('Télécom')
devrait renvoyer 7 en Latin1 et 9 en UTF8
J'ai fait ces 2 petites fonctions qui fonctionnent bien avec la détection/conversion UTF-8 et ISO-8859-1 ...
function detect_encoding($string)
{
//http://w3.org/International/questions/qa-forms-utf-8.html
if (preg_match('%^(?: [\x09\x0A\x0D\x20-\x7E] | [\xC2-\xDF][\x80-\xBF] | \xE0[\xA0-\xBF][\x80-\xBF] | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} | \xED[\x80-\x9F][\x80-\xBF] | \xF0[\x90-\xBF][\x80-\xBF]{2} | [\xF1-\xF3][\x80-\xBF]{3} | \xF4[\x80-\x8F][\x80-\xBF]{2} )*$%xs', $string))
return 'UTF-8';
//If you need to distinguish between UTF-8 and ISO-8859-1 encoding, list UTF-8 first in your encoding_list.
//if you list ISO-8859-1 first, mb_detect_encoding() will always return ISO-8859-1.
return mb_detect_encoding($string, array('UTF-8', 'ASCII', 'ISO-8859-1', 'JIS', 'EUC-JP', 'SJIS'));
}
function convert_encoding($string, $to_encoding, $from_encoding = '')
{
if ($from_encoding == '')
$from_encoding = detect_encoding($string);
if ($from_encoding == $to_encoding)
return $string;
return mb_convert_encoding($string, $to_encoding, $from_encoding);
}
Si votre base de données contient des chaînes dans 2 jeux de caractères différents, ce que je ferais au lieu de harceler tout votre code d'application avec la détection/conversion de jeux de caractères, c'est d'écrire un script "one shot" qui lira tous vos enregistrements de tables et mettra à jour leurs chaînes au bon format (je choisirais UTF-8 si j'étais vous). De cette façon, votre code sera plus propre et plus simple à maintenir.
Il suffit de boucler les enregistrements dans toutes les tables de votre base de données et de convertir des chaînes comme ceci:
//if the 3rd param is not specified the "from encoding" is detected automatically
$newString = convert_encoding($oldString, 'UTF-8');
Je n'ai pas essayé vos échantillons ici, mais d'après les expériences passées, il existe une solution rapide pour cela. Juste après la connexion à la base de données, exécutez la requête suivante AVANT d'exécuter toute autre requête:
SET NAMES UTF8;
Il est conforme à SQL Standard et fonctionne bien avec d'autres bases de données, comme Firebird et PostgreSQL.
Mais rappelez-vous, vous devez également assurer les déclarations UTF-8 sur d'autres endroits afin que votre application fonctionne correctement. Suivez une liste de contrôle rapide.
- Tous les fichiers doivent être enregistrés au format UTF-8 (préféré sans BOM [Byte Order Mask])
- Votre serveur HTTP doit envoyer l'en-tête de codage UTF-8. Utilisez Firebug ou Live HTTP Headers pour inspecter.
- Si votre serveur compresse et/ou tokenise la réponse, vous pouvez voir le contenu de l'en-tête comme fragmenté ou compressé. Ce n'est pas un problème si vous enregistrez vos fichiers au format UTF-8 et
- Déclarez l'encodage dans l'en-tête HTML, en utilisant la balise META appropriée.
- Sur toutes les applications (sockets, système de fichiers, bases de données ...), n'oubliez pas de signaler UTF-8 chaque fois que vous le pouvez. Faire cela lors de l'ouverture d'une connexion à une base de données vous évite d'avoir à encoder/décoder/déboguer tout le temps. Prenez-les par racine.