Je lis beaucoup de textes de divers flux RSS et les insère dans ma base de données.
Bien sûr, plusieurs codages de caractères différents sont utilisés dans les flux, par exemple. UTF-8 et ISO 8859-1.
Malheureusement, il y a parfois des problèmes avec les encodages des textes. Exemple:
Le "ß" dans "Fußball" devrait ressembler à ceci dans ma base de données: "". S'il s'agit d'un "Â", il s'affiche correctement.
Parfois, le "ß" dans "Fußball" ressemble à ceci dans ma base de données: "ÃÂ". Ensuite, il s’affiche à tort, bien sûr.
Dans d'autres cas, le "ß" est enregistré en tant que "ß" - donc sans aucune modification. Ensuite, il est également affiché à tort.
Que puis-je faire pour éviter les cas 2 et 3?
Comment puis-je faire tout le même codage, de préférence UTF-8? Quand dois-je utiliser utf8_encode()
, quand dois-je utiliser utf8_decode()
(il est clair quel est l'effet mais quand dois-je utiliser les fonctions?) Et quand dois-je ne rien faire avec l'entrée?
Comment puis-je tout faire le même encodage? Peut-être avec la fonction mb_detect_encoding()
? Puis-je écrire une fonction pour cela? Donc mes problèmes sont:
Une fonction comme celle-ci fonctionnerait-elle?
function correct_encoding($text) {
$current_encoding = mb_detect_encoding($text, 'auto');
$text = iconv($current_encoding, 'UTF-8', $text);
return $text;
}
Je l'ai testé, mais ça ne marche pas. Qu'est ce qui ne va pas avec ça?
Si vous appliquez utf8_encode()
à une chaîne déjà en UTF-8, la sortie UTF-8 est déformée.
J'ai créé une fonction qui aborde tous ces problèmes. Cela s'appelle Encoding::toUTF8()
.
Vous n'avez pas besoin de savoir quel est l'encodage de vos chaînes. Ce peut être Latin1 ( ISO 8859-1) , Windows-1252 ou UTF-8, ou la chaîne peut en avoir un mélange. Encoding::toUTF8()
convertira tout en UTF-8.
Je l'ai fait parce qu'un service me donnait un flux de données tout mélangé, mélangeant UTF-8 et Latin1 dans la même chaîne.
Usage:
require_once('Encoding.php');
use \ForceUTF8\Encoding; // It's namespaced now.
$utf8_string = Encoding::toUTF8($utf8_or_latin1_or_mixed_string);
$latin1_string = Encoding::toLatin1($utf8_or_latin1_or_mixed_string);
Télécharger:
https://github.com/neitanod/forceutf8
J'ai inclus une autre fonction, Encoding::fixUFT8()
, qui corrigera chaque chaîne UTF-8 qui a l'air tronquée.
Usage:
require_once('Encoding.php');
use \ForceUTF8\Encoding; // It's namespaced now.
$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
J'ai transformé la fonction (forceUTF8
) en une famille de fonctions statiques sur une classe appelée Encoding
. La nouvelle fonction est Encoding::toUTF8()
.
Vous devez d’abord détecter quel encodage a été utilisé. Lors de l'analyse des flux RSS (probablement via HTTP), vous devez lire l'encodage à partir du paramètre charset
du Content-Type
champ d'en-tête HTTP . S'il n'est pas présent, lisez le codage à partir de l'attribut encoding
de instruction de traitement XML . Si cela manque aussi, tilisez UTF-8 comme défini dans la spécification .
Edit Voici ce que je ferais probablement:
Je voudrais utiliser cURL pour envoyer et récupérer la réponse. Cela vous permet de définir des champs d'en-tête spécifiques et d'extraire l'en-tête de la réponse. Après avoir récupéré la réponse, vous devez analyser la réponse HTTP et la scinder en un en-tête et un corps. L'en-tête doit alors contenir le champ d'en-tête Content-Type
qui contient le type MIME et (éventuellement) le paramètre charset
avec également l'encodage/le jeu de caractères. Si ce n’est pas le cas, nous analyserons l’IP PI XML pour détecter la présence de l’attribut encoding
et obtiendrons le codage à partir de là. Si cela manque également, les spécifications XML définissent l’utilisation du codage UTF-8.
$url = 'http://www.lr-online.de/storage/rss/rss/sport.xml';
$accept = array(
'type' => array('application/rss+xml', 'application/xml', 'application/rdf+xml', 'text/xml'),
'charset' => array_diff(mb_list_encodings(), array('pass', 'auto', 'wchar', 'byte2be', 'byte2le', 'byte4be', 'byte4le', 'BASE64', 'UUENCODE', 'HTML-ENTITIES', 'Quoted-Printable', '7bit', '8bit'))
);
$header = array(
'Accept: '.implode(', ', $accept['type']),
'Accept-Charset: '.implode(', ', $accept['charset']),
);
$encoding = null;
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
$response = curl_exec($curl);
if (!$response) {
// error fetching the response
} else {
$offset = strpos($response, "\r\n\r\n");
$header = substr($response, 0, $offset);
if (!$header || !preg_match('/^Content-Type:\s+([^;]+)(?:;\s*charset=(.*))?/im', $header, $match)) {
// error parsing the response
} else {
if (!in_array(strtolower($match[1]), array_map('strtolower', $accept['type']))) {
// type not accepted
}
$encoding = trim($match[2], '"\'');
}
if (!$encoding) {
$body = substr($response, $offset + 4);
if (preg_match('/^<\?xml\s+version=(?:"[^"]*"|\'[^\']*\')\s+encoding=("[^"]*"|\'[^\']*\')/s', $body, $match)) {
$encoding = trim($match[1], '"\'');
}
}
if (!$encoding) {
$encoding = 'utf-8';
} else {
if (!in_array($encoding, array_map('strtolower', $accept['charset']))) {
// encoding not accepted
}
if ($encoding != 'utf-8') {
$body = mb_convert_encoding($body, 'utf-8', $encoding);
}
}
$simpleXML = simplexml_load_string($body, null, LIBXML_NOERROR);
if (!$simpleXML) {
// parse error
} else {
echo $simpleXML->asXML();
}
}
Détecter l'encodage est difficile.
mb_detect_encoding
fonctionne par devinettes, en fonction du nombre de candidats que vous avez réussi. Dans certains codages, certaines séquences d'octets ne sont pas valides et permettent donc de distinguer différents candidats. Malheureusement, il y a beaucoup d'encodages, où les mêmes octets sont valides (mais différents). Dans ces cas, il n’ya aucun moyen de déterminer le codage; Vous pouvez implémenter votre propre logique pour faire des suppositions dans ces cas. Par exemple, les données provenant d'un site japonais pourraient être plus susceptibles d'avoir un codage japonais.
Tant que vous ne traitez que des langues d'Europe occidentale, les trois principaux codages à considérer sont utf-8
, iso-8859-1
et cp-1252
. Comme ce sont des valeurs par défaut pour de nombreuses plates-formes, elles sont également les plus susceptibles d’être mal signalées. Par exemple. Si les utilisateurs utilisent des encodages différents, ils seront probablement francs à ce sujet, car sinon leur logiciel tomberait en panne très souvent. Par conséquent, une bonne stratégie consiste à faire confiance au fournisseur, sauf si le codage est indiqué comme l’un de ces trois. Vous devez tout de même vérifier sa validité, en utilisant mb_check_encoding
(notez que valide n'est pas identique à être - la même entrée peut être valide pour de nombreux encodages ) Si c'est un de ceux-là, vous pouvez alors utiliser mb_detect_encoding
pour les distinguer. Heureusement, c'est assez déterministe; Vous devez juste utiliser la séquence de détection appropriée, qui est UTF-8,ISO-8859-1,WINDOWS-1252
.
Une fois que vous avez détecté le codage, vous devez le convertir en représentation interne (UTF-8
est le seul choix judicieux). La fonction utf8_encode
transforme ISO-8859-1
en UTF-8
, de sorte qu'elle ne peut être utilisée que pour ce type d'entrée particulier. Pour les autres encodages, utilisez mb_convert_encoding
.
A vraiment Une bonne façon de mettre en œuvre une fonction isUTF8
- peut être trouvée sur php.net :
function isUTF8($string) {
return (utf8_encode(utf8_decode($string)) == $string);
}
Cette feuille de notes recense certaines mises en garde courantes liées à la manipulation de UTF-8 en PHP: http://developer.loftdigital.com/blog/php-utf-8-cheatsheet
Cette fonction de détection des caractères multi-octets dans une chaîne pourrait également s'avérer utile ( source ):
function detectUTF8($string)
{
return preg_match('%(?:
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
|\xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
|\xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
|\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
|[\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
|\xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)+%xs',
$string);
}
Un petit heads-up. Vous avez dit que le "ß" devrait être affiché comme "" dans votre base de données.
Ceci est probablement dû au fait que vous utilisez une base de données avec un codage de caractères Latin-1 ou que votre connexion PHP-MySQL est mal définie. P pense que votre MySQL est configuré pour utiliser UTF-8. Il envoie donc les données au format UTF-8. , mais votre MySQL pense que PHP envoie des données codées ISO 8859-1. Il est donc possible qu’il tente à nouveau de coder vos données envoyées au format UTF-8, ce qui cause ce type de problème.
Jetez un coup d'oeil à mysql_set_charset. Cela peut vous aider.
Vous devez tester le jeu de caractères lors de la saisie car les réponses peuvent être codées avec différents codages.
Je force tout le contenu envoyé dans UTF-8 en effectuant la détection et la traduction en utilisant la fonction suivante:
function fixRequestCharset()
{
$ref = array(&$_GET, &$_POST, &$_REQUEST);
foreach ($ref as &$var)
{
foreach ($var as $key => $val)
{
$encoding = mb_detect_encoding($var[$key], mb_detect_order(), true);
if (!$encoding)
continue;
if (strcasecmp($encoding, 'UTF-8') != 0)
{
$encoding = iconv($encoding, 'UTF-8', $var[$key]);
if ($encoding === false)
continue;
$var[$key] = $encoding;
}
}
}
}
Cette routine transformera toutes les variables PHP provenant de l'hôte distant en UTF-8.
Ou ignorez la valeur si le codage n'a pas pu être détecté ou converti.
Vous pouvez le personnaliser selon vos besoins.
Il suffit de l'invoquer avant d'utiliser les variables.
La chose intéressante à propos de mb_detect_encoding
et mb_convert_encoding
est que l'ordre des codages que vous suggérez est important:
// $input is actually UTF-8
mb_detect_encoding($input, "UTF-8", "ISO-8859-9, UTF-8");
// ISO-8859-9 (WRONG!)
mb_detect_encoding($input, "UTF-8", "UTF-8, ISO-8859-9");
// UTF-8 (OK)
Vous pouvez donc utiliser un ordre spécifique lors de la spécification des codages attendus. Néanmoins, gardez à l’esprit que cela n’est pas infaillible.
Votre encodage ressemble à celui que vous avez encodé en UTF-8 deux fois ; c'est-à-dire, à partir d'un autre codage, en UTF-8, et encore en UTF-8. Comme si vous aviez ISO 8859-1, converti d'ISO 8859-1 en UTF-8 et traité la nouvelle chaîne comme ISO 8859-1 pour une autre conversion en UTF-8.
Voici un pseudocode de ce que vous avez fait:
$inputstring = getFromUser();
$utf8string = iconv($current_encoding, 'utf-8', $inputstring);
$flawedstring = iconv($current_encoding, 'utf-8', $utf8string);
Tu devrais essayer:
mb_detect_encoding()
ou de ce que vous préférez utiliserCela suppose que, dans la conversion "intermédiaire", vous avez utilisé ISO 8859-1. Si vous avez utilisé Windows-1252, convertissez-le en Windows-1252 (latin1). L'encodage source d'origine n'est pas important; celui que vous avez utilisé dans imparfait, la seconde conversion est.
C'est ce que je pense de ce qui s'est passé. il y a très peu de choses que vous auriez pu faire pour obtenir quatre octets à la place d'un octet étendu ASCII.
La langue allemande utilise également ISO 8859-2 et Windows-125 (Latin-2).
Travailler sur l'encodage des caractères des flux RSS semble être compliqué . Même les pages Web normales omettent souvent, ou mentent, leur codage.
Vous pouvez donc essayer d'utiliser le bon moyen pour détecter le codage, puis recourir à une forme de détection automatique (devinette).
Je sais que c'est une question plus ancienne, mais je pense qu'une réponse utile ne fera jamais de mal. J'avais des problèmes avec mon encodage entre une application de bureau, les variables SQLite et GET/POST. Certains seraient en UTF-8, d'autres en ASCII et, fondamentalement, tout serait foiré lorsque des personnages étrangers seraient impliqués.
Voici ma solution. Il nettoie votre GET/POST/REQUEST (j’ai omis les cookies, mais vous pouvez les ajouter si vous le souhaitez) à chaque chargement de page avant le traitement. Cela fonctionne bien dans un en-tête. PHP émettra des avertissements s'il ne parvient pas à détecter automatiquement le codage source. Ces avertissements sont donc supprimés avec les @.
//Convert everything in our vars to UTF-8 for playing Nice with the database...
//Use some auto detection here to help us not double-encode...
//Suppress possible warnings with @'s for when encoding cannot be detected
try
{
$process = array(&$_GET, &$_POST, &$_REQUEST);
while (list($key, $val) = each($process)) {
foreach ($val as $k => $v) {
unset($process[$key][$k]);
if (is_array($v)) {
$process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = $v;
$process[] = &$process[$key][@mb_convert_encoding($k,'UTF-8','auto')];
} else {
$process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = @mb_convert_encoding($v,'UTF-8','auto');
}
}
}
unset($process);
}
catch(Exception $ex){}
Je cherchais des solutions d’encodage depuis âges , et cette page est probablement la conclusion de nombreuses années de recherche! J'ai testé certaines des suggestions que vous avez mentionnées et voici mes notes:
Ceci est ma chaîne de test:
c'est une corde "écrite", j'ai acheté des morceaux spéciaux pour les voir, convertis par musique !! & c'est ça!
Je fais un INSERT pour enregistrer cette chaîne sur une base de données dans un champ défini comme utf8_general_ci
Le jeu de caractères de ma page est UTF-8.
Si je fais un INSERT juste comme ça, dans ma base de données, j'ai des caractères venant probablement de Mars ...
J'ai donc besoin de les convertir en UTF-8 "raisonnable". J'ai essayé utf8_encode()
, mais des caractères extraterrestres envahissaient toujours ma base de données ...
J'ai donc essayé d'utiliser la fonction forceUTF8
postée au numéro 8, mais dans la base de données, la chaîne sauvegardée ressemble à ceci:
c'est une chaîne "wrà © wrÃtten" bà ¢ Je ne suis pas allé au plan spécial "spécial pour voir", converti par fonctón !! & c'est ça!
Donc, en collectant quelques informations supplémentaires sur cette page et en les fusionnant avec d'autres informations sur d'autres pages, j'ai résolu mon problème avec cette solution:
$finallyIDidIt = mb_convert_encoding(
$string,
mysql_client_encoding($resourceID),
mb_detect_encoding($string)
);
Maintenant, dans ma base de données, ma chaîne est encodée correctement.
NOTE: La seule note à prendre en compte est en fonction mysql_client_encoding
! Vous devez être connecté à la base de données, car cette fonction souhaite un ID de ressource en tant que paramètre.
Mais bon, je fais juste ce ré-encodage avant mon INSERT alors pour moi ce n’est pas un problème.
C'est simple: quand vous obtenez quelque chose qui n'est pas UTF-8, vous devez encoder ça en UTF-8.
Ainsi, lorsque vous récupérez un certain flux conforme à ISO 8859-1, analysez-le dans utf8_encode
.
Cependant, si vous récupérez un flux UTF-8, vous n'avez rien à faire.
php.net/ mb_detect_encoding
_echo mb_detect_encoding($str, "auto");
_
ou
_echo mb_detect_encoding($str, "UTF-8, ASCII, ISO-8859-1");
_
je ne sais vraiment pas quels sont les résultats, mais je vous suggère de ne prendre que certains de vos flux avec différents encodages et d'essayer si _mb_detect_encoding
_ fonctionne ou non.
mettre à jour
auto est l'abréviation de "ASCII, JIS, UTF-8, EUC-JP, SJIS". il retourne le jeu de caractères détecté, que vous pouvez utiliser pour convertir la chaîne en utf-8 avec iconv .
_<?php
function convertToUTF8($str) {
$enc = mb_detect_encoding($str);
if ($enc && $enc != 'UTF-8') {
return iconv($enc, 'UTF-8', $str);
} else {
return $str;
}
}
?>
_
je ne l'ai pas testé, donc aucune garantie. et peut-être il y a un moyen plus simple.
@harpax qui a fonctionné pour moi. Dans mon cas, cela suffit:
if (isUTF8($str)) {
echo $str;
}
else
{
echo iconv("ISO-8859-1", "UTF-8//TRANSLIT", $str);
}
Ÿ
est Mojibake pour ß
. Dans votre base de données, vous pouvez avoir hex
DF if the column is "latin1",
C39F if the column is utf8 -- OR -- it is latin1, but "double-encoded"
C383C5B8 if double-encoded into a utf8 column
Vous devriez ne pas utiliser les fonctions d'encodage/décodage en PHP; au lieu de cela, vous devez configurer la base de données et la connexion à celle-ci correctement.
Si MySQL est impliqué, voyez: Problème avec les caractères utf8; ce que je vois n'est pas ce que j'ai stocké
Après avoir réglé vos scripts php, n'oubliez pas de dire à mysql quel jeu de caractères vous transmettez et que vous souhaitez recevoir.
Exemple: définir le jeu de caractères utf8
Le fait de transmettre des données utf8 à une table latin1 lors d’une session d’E/S latin1 donne ces oiseaux voleurs désagréables. Je le vois tous les jours dans les magasins oscommerce. En quatrième et quatrième position, cela peut sembler juste. Mais phpmyadmin va montrer la vérité. En indiquant à mysql quel jeu de caractères vous transmettez, il gérera pour vous la conversion des données mysql.
Comment récupérer des données mysql cryptées existantes est un autre sujet à discuter. :)
Obtenez l'encodage à partir des en-têtes et convertissez-le en utf-8.
$post_url='http://website.domain';
/// Get headers ////////////////////////////////////////////////////////////
function get_headers_curl($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
$r = curl_exec($ch);
return $r;
}
$the_header = get_headers_curl($post_url);
/// check for redirect /////////////////////////////////////////////////
if (preg_match("/Location:/i", $the_header)) {
$arr = explode('Location:', $the_header);
$location = $arr[1];
$location=explode(chr(10), $location);
$location = $location[0];
$the_header = get_headers_curl(trim($location));
}
/// Get charset /////////////////////////////////////////////////////////////////////
if (preg_match("/charset=/i", $the_header)) {
$arr = explode('charset=', $the_header);
$charset = $arr[1];
$charset=explode(chr(10), $charset);
$charset = $charset[0];
}
///////////////////////////////////////////////////////////////////////////////
// echo $charset;
if($charset && $charset!='UTF-8') { $html = iconv($charset, "UTF-8", $html); }
La réponse la plus votée ne fonctionne pas. Voici le mien et j'espère que ça aide.
function toUTF8($raw) {
try{
return mb_convert_encoding($raw, "UTF-8", "auto");
}catch(\Exception $e){
return mb_convert_encoding($raw, "UTF-8", "GBK");
}
}
Cette version est pour la langue allemande mais vous pouvez modifier les $ CHARSETS et les $ TESTCHARS
class CharsetDetector
{
private static $CHARSETS = array(
"ISO_8859-1",
"ISO_8859-15",
"CP850"
);
private static $TESTCHARS = array(
"€",
"ä",
"Ä",
"ö",
"Ö",
"ü",
"Ü",
"ß"
);
public static function convert($string)
{
return self::__iconv($string, self::getCharset($string));
}
public static function getCharset($string)
{
$normalized = self::__normalize($string);
if(!strlen($normalized))return "UTF-8";
$best = "UTF-8";
$charcountbest = 0;
foreach (self::$CHARSETS as $charset) {
$str = self::__iconv($normalized, $charset);
$charcount = 0;
$stop = mb_strlen( $str, "UTF-8");
for( $idx = 0; $idx < $stop; $idx++)
{
$char = mb_substr( $str, $idx, 1, "UTF-8");
foreach (self::$TESTCHARS as $testchar) {
if($char == $testchar)
{
$charcount++;
break;
}
}
}
if($charcount>$charcountbest)
{
$charcountbest=$charcount;
$best=$charset;
}
//echo $text."<br />";
}
return $best;
}
private static function __normalize($str)
{
$len = strlen($str);
$ret = "";
for($i = 0; $i < $len; $i++){
$c = ord($str[$i]);
if ($c > 128) {
if (($c > 247)) $ret .=$str[$i];
elseif ($c > 239) $bytes = 4;
elseif ($c > 223) $bytes = 3;
elseif ($c > 191) $bytes = 2;
else $ret .=$str[$i];
if (($i + $bytes) > $len) $ret .=$str[$i];
$ret2=$str[$i];
while ($bytes > 1) {
$i++;
$b = ord($str[$i]);
if ($b < 128 || $b > 191) {$ret .=$ret2; $ret2=""; $i+=$bytes-1;$bytes=1; break;}
else $ret2.=$str[$i];
$bytes--;
}
}
}
return $ret;
}
private static function __iconv($string, $charset)
{
return iconv ( $charset, "UTF-8" , $string );
}
}
Je trouve la solution ici http://deer.org.ua/2009/10/06/1/
class Encoding
{
/**
* http://deer.org.ua/2009/10/06/1/
* @param $string
* @return null
*/
public static function detect_encoding($string)
{
static $list = ['utf-8', 'windows-1251'];
foreach ($list as $item) {
try {
$sample = iconv($item, $item, $string);
} catch (\Exception $e) {
continue;
}
if (md5($sample) == md5($string)) {
return $item;
}
}
return null;
}
}
$content = file_get_contents($file['tmp_name']);
$encoding = Encoding::detect_encoding($content);
if ($encoding != 'utf-8') {
$result = iconv($encoding, 'utf-8', $content);
} else {
$result = $content;
}
Je pense que @ est une mauvaise décision et apporte quelques modifications à la solution de deer.org.ua;