Détectez l'encodage et rendez tout UTF-8

304

Je lis beaucoup de textes de divers flux RSS et je 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:

  1. Le "ß" dans "Fußball" devrait ressembler à ceci dans ma base de données: "Ÿ". S'il s'agit d'un "Ÿ", il s'affiche correctement.

  2. Parfois, le "ß" dans "Fußball" ressemble à ceci dans ma base de données: "ß". Ensuite, il est affiché à tort, bien sûr.

  3. 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 encodage, de préférence UTF-8? Quand dois-je utiliser utf8_encode(), quand dois-je utiliser utf8_decode()(c'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 faire tout le même encodage? Peut-être avec la fonction mb_detect_encoding()? Puis-je écrire une fonction pour cela? Mes problèmes sont donc:

  1. Comment savoir quel encodage le texte utilise?
  2. Comment le convertir en UTF-8 - quel que soit l'ancien encodage?

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 cela ne fonctionne pas. Qu'est ce qui ne va pas avec ça?

croasser
la source
36
"Le" ß "dans" Fußball "devrait ressembler à ceci dans ma base de données:" Ÿ ".". Non, cela devrait ressembler à ß. Assurez-vous que le classement et la connexion sont correctement configurés. Sinon, le tri et la recherche seront interrompus pour vous.
Rich Bradshaw
5
Votre base de données est mal configurée. Si vous souhaitez stocker du contenu Unicode, configurez-le simplement pour cela. Ainsi, au lieu d'essayer de contourner le problème dans votre code PHP, vous devez d'abord corriger la base de données.
dolmen
2
UTILISATION: $ from = mb_detect_encoding ($ text); $ text = mb_convert_encoding ($ text, 'UTF-8', $ from);
Informate.it

Réponses:

363

Si vous appliquez utf8_encode()à une chaîne déjà UTF-8, elle renverra une sortie UTF-8 tronquée.

J'ai créé une fonction qui traite de toutes ces questions. Ça s'appelle Encoding::toUTF8().

Vous n'avez pas besoin de savoir quel est l'encodage de vos chaînes. Il peut s'agir de 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 foiré, 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 semble 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().

Sebastián Grignoli
la source
1
Eh bien, si vous regardez le code, fixUTF8 appelle simplement forceUTF8 une fois de plus jusqu'à ce que la chaîne soit retournée inchangée. Un appel à fixUTF8 () prend au moins deux fois le temps d'un appel à forceUTF8 (), il est donc beaucoup moins performant. J'ai créé fixUTF8 () juste pour créer un programme en ligne de commande qui corrigerait les fichiers "corrompus par codage", mais dans un environnement en direct, il est rarement nécessaire.
Sebastián Grignoli
3
Comment cela convertit-il les caractères non UTF8 en UTF8, sans savoir dans quel encodage les caractères non valides commencent?
philfreo
4
Il suppose ISO-8859-1, la réponse le dit déjà. La seule différence entre forceUTF8 () et utf8_encode () est que forceUTF8 () reconnaît les caractères UTF8 et les garde inchangés.
Sebastián Grignoli
28
"Vous n'avez pas besoin de savoir quel est l'encodage de vos chaînes." - Je suis en désaccord. Deviner et essayer peut fonctionner, mais vous rencontrerez toujours tôt ou tard des cas limites où ce n'est pas le cas.
deceze
4
Je suis tout à fait d'accord. En fait, je ne voulais pas dire qu'en règle générale, expliquez simplement que ce cours pourrait vous aider si c'est la situation dans laquelle vous vous trouvez.
Sebastián Grignoli
74

Vous devez d'abord détecter quel encodage a été utilisé. Lorsque vous analysez des flux RSS (probablement via HTTP), vous devez lire l'encodage à partir du charsetparamètre du Content-Typechamp d'en-tête HTTP . S'il n'est pas présent, lisez le codage à partir de l' encodingattribut de l' instruction de traitement XML . Si cela manque également, utilisez UTF-8 comme défini dans la spécification .


Modifier    Voici ce que je ferais probablement:

J'utiliserais 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 de récupérer également l'en-tête de réponse. Après avoir récupéré la réponse, vous devez analyser la réponse HTTP et la diviser en en-tête et corps. L'en-tête doit alors contenir le Content-Typechamp d'en-tête qui contient le type MIME et (espérons-le) lecharset paramètre avec l'encodage / charset aussi. Sinon, nous analyserons le PI XML pour la présence de l' encodingattribut et obtiendrons l'encodage à partir de là. Si cela manque également, les spécifications XML définissent l'utilisation de l'UTF-8 comme encodage.

$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();
    }
}
Gombo
la source
Merci. Ce serait facile. Mais cela fonctionnerait-il vraiment? Il y a souvent des encodages incorrects donnés dans les en-têtes HTTP ou dans les attributs de XML.
caw
25
Encore une fois: ce n'est pas votre problème. Des normes ont été établies pour éviter de tels problèmes. Si les autres ne les suivent pas, c'est leur problème, pas le vôtre.
Gumbo
Ok, je pense que tu m'as finalement convaincu maintenant. :)
caw
Merci pour le code. Mais pourquoi ne pas simplement l'utiliser? paste.bradleygill.com/index.php?paste_id=9651 Votre code est beaucoup plus complexe, quoi de mieux avec lui?
caw
Eh bien, premièrement, vous faites deux demandes, une pour l'en-tête HTTP et une pour les données. Deuxièmement, vous êtes à la recherche de toute apparence de charset=et encoding=et non pas seulement aux positions appropriées. Et troisièmement, vous ne vérifiez pas si l'encodage déclaré est accepté.
Gumbo
39

La détection de l'encodage est difficile.

mb_detect_encodingfonctionne en devinant, sur la base d'un certain nombre de candidats que vous réussissez. Dans certains codages, certaines séquences d'octets ne sont pas valides et peuvent donc distinguer différents candidats. Malheureusement, il existe de nombreux encodages, où les mêmes octets sont valides (mais différents). Dans ces cas, il n'y a aucun moyen de déterminer l'encodage; Vous pouvez implémenter votre propre logique pour faire des suppositions dans ces cas. Par exemple, les données provenant d'un site japonais peuvent être plus susceptibles d'avoir un encodage japonais.

Tant que vous ne vous occupez que des langues d'Europe occidentale, les trois principaux encodages à considérer sont utf-8, iso-8859-1et cp-1252. Étant donné qu'il s'agit de valeurs par défaut pour de nombreuses plates-formes, elles sont également les plus susceptibles d'être signalées à tort. Par exemple. si les gens utilisent des encodages différents, ils seront probablement honnêtes à 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 signalé comme l'un de ces trois. Vous devriez toujours vérifier qu'il est en effet valide, en utilisant mb_check_encoding(notez que valide n'est pas la même chose qu'être - la même entrée peut être valide pour de nombreux encodages). S'il s'agit de l'un d'entre eux, vous pouvez alors utilisermb_detect_encodingpour les distinguer. Heureusement, c'est assez déterministe; Vous avez juste besoin d'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é l'encodage, vous devez le convertir en votre représentation interne ( UTF-8est le seul choix sensé). La fonction se utf8_encodetransforme ISO-8859-1en UTF-8, elle ne peut donc être utilisée que pour ce type d'entrée particulier. Pour les autres encodages, utilisez mb_convert_encoding.

troelskn
la source
Merci beaucoup! Quoi de mieux: mb-convert-encoding () ou iconv ()? Je ne connais pas les différences. Oui, je n'aurai qu'à analyser les langues d'Europe occidentale, en particulier l'anglais, l'allemand et le français.
caw
7
Je viens de voir: mb-detect-encoding () est inutile. Il prend uniquement en charge UTF-8, UTF-7, ASCII, EUC-JP, SJIS, eucJP-win, SJIS-win, JIS et ISO-2022-JP. Les plus importants pour moi, ISO-8859-1 et WINDOWS-1252, ne sont pas pris en charge. Je ne peux donc pas utiliser mb-detect-encoding ().
caw
1
Vous avez raison. Cela fait un moment que je ne l'ai pas utilisé. Vous devrez alors écrire votre propre code de détection ou utiliser un utilitaire externe. UTF-8 peut être déterminé de manière assez fiable, car ses séquences d'échappement sont assez caractéristiques. wp-1252 et iso-8859-1 peuvent être distingués car wp-1252 peut contenir des octets illégaux dans iso-8859-1. Utilisez Wikipedia pour obtenir les détails, ou regardez dans la section commentaires de php.net, sous diverses fonctions liées aux charset.
troelskn
Je pense que vous pouvez distinguer les différents encodages quand vous regardez les formes dans lesquelles les chants spéciaux émergent: Le "ß" allemand émerge sous différentes formes: Parfois "Ÿ", parfois "ß" et parfois "ß". Pourquoi?
caw
Oui, mais vous devez ensuite connaître le contenu de la chaîne avant de la comparer, et ce type de résultat défait l'objectif en premier lieu. Le ß allemand apparaît différemment car il a des valeurs différentes dans différents codages. Certains caractères sont représentés de la même manière dans différents encodages (par exemple, tous les caractères du jeu de caractères ascii sont encodés de la même manière dans utf-8, iso-8859- * et wp-1252), tant que vous utilisez juste ces personnages, ils se ressemblent tous. C'est pourquoi ils sont parfois appelés compatibles ascii.
troelskn
14

Un très bon moyen d'implémenter une isUTF8fonction peut être trouvé sur php.net :

function isUTF8($string) {
    return (utf8_encode(utf8_decode($string)) == $string);
}
harpax
la source
16
Malheureusement, cela ne fonctionne que lorsque la chaîne se compose uniquement de caractères inclus dans ISO-8859-1. Mais cela pourrait fonctionner: @iconv ('utf-8', 'utf-8 // IGNORE', $ str) == $ str
Christian Davén
@Christian: En effet, c'est ce que les auteurs de High Performance MySQL recommandent également.
Alix Axel
1
Cela ne fonctionne pas correctement: echo (int) isUTF8 ('z'); # 1 echo (int) isUTF8 (NULL); # 1
Yousha Aleayoub
1
Bien que n'étant pas parfait, je pense que c'est une bonne façon de mettre en œuvre une vérification UTF-8 sommaire.
Mateng
1
mb_check_encoding($string, 'UTF-8')
décomposer
13

Ce cheatsheet répertorie quelques mises en garde courantes liées à la gestion UTF-8 en PHP: http://developer.loftdigital.com/blog/php-utf-8-cheatsheet

Cette fonction détectant les caractères multi-octets dans une chaîne peut é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);
}

miek
la source
2
Je pense que cela ne fonctionne pas correctement: echo detectUTF8 ('3٣3'); # 1
Yousha Aleayoub
10

Un peu de tête. Vous avez dit que le "ß" devrait être affiché comme "Â" "dans votre base de données.

C'est probablement parce que vous utilisez une base de données avec un codage de caractères Latin-1 ou peut-être que votre connexion PHP-MySQL est incorrecte, c'est-à-dire que P pense que votre MySQL est configuré pour utiliser UTF-8, donc il envoie des données en UTF-8 , mais votre serveur MySQL pense que PHP envoie des données encodées en ISO 8859-1, il peut donc à nouveau essayer de coder vos données envoyées en UTF-8, provoquant ce genre de problème.

Jetez un œil à mysql_set_charset . Cela peut vous aider.

Krynble
la source
4

Votre encodage ressemble à celui que vous avez encodé deux fois en UTF-8 ; 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:

  1. détecter l'encodage en utilisant mb_detect_encoding()ou tout ce que vous souhaitez utiliser
  2. si c'est UTF-8, convertissez-le en ISO 8859-1 et répétez l'étape 1
  3. enfin, reconvertissez en UTF-8

Cela suppose que dans la conversion "moyenne", 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 la deuxième conversion défectueuse est.

C'est ma conjecture sur 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 ASCII étendu.

La langue allemande utilise également ISO 8859-2 et Windows-1250 (Latin-2).

Ivan Vučica
la source
3

La chose intéressante à propos de mb_detect_encodinget mb_convert_encodingest que l'ordre des encodages que vous proposez importe:

// $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)

Donc, vous voudrez peut-être utiliser un ordre spécifique lors de la spécification des encodages attendus. N'oubliez pas que ce n'est pas infaillible.

Halil Özgür
la source
2
Cela se produit car ISO-8859-9 accepte en pratique toute entrée binaire. Il en va de même pour Windows-1252 et ses amis. Vous devez d'abord tester les encodages qui peuvent ne pas accepter l'entrée.
Mikko Rantalainen
@MikkoRantalainen, oui, je suppose que cette partie des documents dit quelque chose de similaire: php.net/manual/en/function.mb-detect-order.php#example-2985
Halil Özgür
Étant donné que la spécification HTML WHATWG définit Windows 1252 comme codage par défaut, il devrait être assez sûr de le supposer if ($input_is_not_UTF8) $input_is_windows1252 = true;. Voir aussi: html.spec.whatwg.org/multipage/…
Mikko Rantalainen
3

Vous devez tester le jeu de caractères en entrée car les réponses peuvent être codées avec différents encodages.

Je force tout le contenu à être envoyé en 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.

cavila
la source
quel est le but d'utiliser mb_detect_order () sans une liste de codage passée?
giorgio79
Le but est de renvoyer le tableau ordonné d'encodages configuré par le système défini dans php.ini utilisé. Ceci est requis par mb_detect_encoding pour remplir le troisième paramètre.
cavila
2

L'élaboration du codage des caractères des flux RSS semble être compliquée . Même les pages Web normales omettent souvent, ou mentent, leur encodage.

Vous pouvez donc essayer d'utiliser la bonne façon de détecter l'encodage, puis revenir à une forme d'auto-détection (deviner).

Kevin ORourke
la source
Je ne veux pas lire l'encodage à partir des informations de flux. Il est donc égal si les informations de flux sont incorrectes. Je voudrais détecter l'encodage du texte.
caw
@ marco92w: Ce n'est pas votre problème si l'encodage déclaré est incorrect. Les normes n'ont pas été établies pour le plaisir.
Gumbo
1
@Gumbo: mais si vous travaillez dans le monde réel, vous devez être capable de gérer des choses comme des encodages déclarés incorrects. Le problème est qu'il est très difficile de deviner (correctement) l'encodage uniquement à partir d'un texte. Les normes sont merveilleuses, mais beaucoup (la plupart?) Des pages / flux ne les respectent pas.
Kevin ORourke
@Kevin ORourke: Exactement, à droite. C'est mon problème. @ Gumbo: Oui, c'est mon problème. Je veux lire les flux et les agréger. Je dois donc corriger les mauvais encodages.
caw
@ marco92w: Mais vous ne pouvez pas corriger l'encodage si vous ne connaissez pas l'encodage correct et l'encodage actuel. Et c'est à cela que sert la déclaration charset/ encoding: décrivez l'encodage dans lequel les données sont encodées.
Gumbo
2

Je sais que c'est une question plus ancienne, mais je pense qu'une réponse utile ne fait jamais de mal. J'avais des problèmes avec mon encodage entre une application de bureau, SQLite et les variables GET / POST. Certains seraient en UTF-8, certains en ASCII et, fondamentalement, tout serait foutu lorsque des personnages étrangers seraient impliqués.

Voici ma solution. Il efface 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 enverra des avertissements s'il ne peut pas détecter l'encodage source automatiquement, donc ces avertissements sont supprimés avec @.

//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){}
jocull
la source
Merci pour la réponse, jocull. La fonction mb_convert_encoding () est ce que nous avons déjà eu ici, non? ;) La seule nouveauté dans votre réponse est donc les boucles permettant de changer l'encodage dans toutes les variables.
caw
2

Je cherchais des solutions pour l'encodage depuis des siècles , et cette page est probablement la conclusion d'années de recherche! J'ai testé certaines des suggestions que vous avez mentionnées et voici mes notes:

Voici ma chaîne de test:

c'est une ficelle "wròng wrìtten" bùt je nèed à pù 'sòme' spécial chàrs pour voir thèm, convertèd par fùnctìon !! & c'est tout!

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 comme ça, dans ma base de données, j'ai des personnages qui viennent probablement de Mars ...

J'ai donc besoin de les convertir en un UTF-8 "sain". 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 forceUTF8publiée sur le numéro 8, mais dans la base de données la chaîne enregistrée ressemble à ceci:

il s'agit d'une chaîne "WrÃngng Wrìtten", mais je suis obligé de pù 'sòme' chà rs spà © ciales pour voir thèm, convertèd par fùnctìon !! & c'est tout!

Donc, en collectant plus d'informations 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, j'ai ma chaîne avec un encodage correct.

REMARQUE: seule la note à prendre en compte est en fonction mysql_client_encoding! Vous devez être connecté à la base de données, car cette fonction veut un ID de ressource comme paramètre.

Mais bon, je fais juste ce ré-encodage avant mon INSERT donc pour moi ce n'est pas un problème.

Mauro
la source
1
Pourquoi n'utilisez-vous pas simplement l' UTF-8encodage client pour mysql en premier lieu? N'aurait pas besoin d'une conversion manuelle de cette façon
Esailija
2

Il est simple: quand vous obtenez quelque chose qui ne UTF-8, vous devez encoder que dans UTF-8.

Ainsi, lorsque vous récupérez un certain flux qui est ISO 8859-1, analysez-le utf8_encode.

Cependant, si vous récupérez un flux UTF-8, vous n'avez rien à faire.

Seb
la source
Merci! OK, je peux découvrir comment le flux est codé en utilisant mb-detect-encoding (), non? Mais que puis-je faire si le flux est ASCII? utf8-encode () est juste pour ISO-8859-1 à UTF-8, n'est-ce pas?
caw
ASCII est un sous-ensemble de ISO-8859-1 ET UTF-8, donc l'utilisation d'utf8-encode () ne devrait pas faire de changement - SI c'est en fait juste ASCII
Michael Borgwardt
Je peux donc toujours utiliser utf8_encode si ce n'est pas UTF-8? Ce serait vraiment facile. Le texte qui était ASCII selon mb-detect-encoding () contenait "& # 228;". S'agit-il d'un caractère ASCII? Ou est-ce HTML?
caw
C'est du HTML. En fait, c'est encodé, donc quand vous l'imprimez dans une page donnée, ça montre bien. Si vous le souhaitez, vous pouvez d'abord ut8_encode () puis html_entity_decode ().
Seb
1
Le caractère ß est codé en UTF-8 avec la séquence d'octets 0xC39F. Interprétée avec Windows-1252, cette séquence représente les deux caractères  (0xC3) et Ÿ (0x9F). Et si vous encodez à nouveau cette séquence d'octets avec UTF-8, vous obtiendrez 0xC383 0xC29F ce qui représente ß dans Windows-1252. Donc, votre erreur est de traiter ces données encodées UTF-8 comme quelque chose avec un encodage autre que UTF-8. Que cette séquence d'octets soit présentée comme le caractère que vous voyez n'est qu'une question d'interprétation. Si vous utilisez un autre encodage / jeu de caractères, vous verrez probablement d'autres caractères.
Gumbo
1

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 simplement prendre certains de vos flux avec différents encodages et d'essayer si cela mb_detect_encodingfonctionne ou non.

update
auto est l'abréviation de "ASCII, JIS, UTF-8, EUC-JP, SJIS". il renvoie 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 qu'il existe un moyen plus simple.

stefs
la source
Je vous remercie. Quelle est la différence entre 'auto' et 'UTF-8, ASCII, ISO-8859-1' comme deuxième argument? 'Auto' propose-t-il plus d'encodages? Il serait alors préférable d'utiliser "auto", non? Si cela fonctionne vraiment sans bugs, je dois seulement changer "ASCII" ou "ISO-8859-1" en "UTF-8". Comment?
caw
2
Votre fonction ne fonctionne pas bien dans tous les cas. Parfois, j'obtiens une erreur: Remarque: iconv (): Détection d'un caractère illégal dans la chaîne d'entrée dans ...
caw
1

@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);
}
PJ Brunet
la source
0

Après avoir trié vos scripts php, n'oubliez pas de dire à mysql quel jeu de caractères vous passez et que vous souhaitez recevoir.

Exemple: jeu de caractères set utf8

Passer des données utf8 à une table latin1 dans une session d'E / S latin1 donne ces pièges à oiseaux. Je vois cela tous les deux jours dans les magasins oscommerce. En arrière et quatrième, cela peut sembler juste. Mais phpmyadmin montrera la vérité. En disant à mysql quel jeu de caractères vous passez, il gérera la conversion des données mysql pour vous.

Comment récupérer des données mysql brouillées existantes est un autre sujet à discuter. :)

Tim
la source
0

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 );
}
}

Lukas Gottschall
la source
0

Obtenez l'encodage 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); }
Arsen
la source
0

Ÿest Mojibake ß. 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 ne devez pas utiliser de fonctions d'encodage / décodage en PHP; à la place, vous devez configurer correctement la base de données et la connexion à celle-ci.

Si MySQL est impliqué, voir: Problème avec les caractères utf8; ce que je vois n'est pas ce que j'ai stocké

Rick James
la source
0

Je trouve une 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 apporter des modifications à la solution de deer.org.ua;

distancer
la source
0

La réponse la plus votée ne fonctionne pas. Voici le mien et j'espère que cela vous aidera.

function toUTF8($raw) {
    try{
        return mb_convert_encoding($raw, "UTF-8", "auto"); 
    }catch(\Exception $e){
        return mb_convert_encoding($raw, "UTF-8", "GBK"); 
    }
}
ch271828n
la source
1
Avez-vous une idée de la raison ou de la différence de vos fichiers? Quelles parties n'ont pas fonctionné pour vous? Par exemple: les caractères allemands majuscules ne se sont pas convertis correctement. Curieux, qu'est-ce que "GBK"?
SherylHohman
-1

Lorsque vous essayez de gérer plusieurs langues comme le japonais et le coréen, vous risquez d'avoir des problèmes. mb_convert_encoding avec le paramètre 'auto' ne fonctionne pas bien. La définition de mb_detect_order ('ASCII, UTF-8, JIS, EUC-JP, SJIS, EUC-KR, UHC') n'aide pas car il détectera à tort EUC- *.

J'ai conclu que tant que les chaînes d'entrée proviennent de HTML, il devrait utiliser «charset» dans un méta-élément. J'utilise Simple HTML DOM Parser car il prend en charge le code HTML non valide.

L'extrait ci-dessous extrait l'élément de titre d'une page Web. Si vous souhaitez convertir une page entière, vous pouvez supprimer certaines lignes.

<?php
require_once 'simple_html_dom.php';

echo convert_title_to_utf8(file_get_contents($argv[1])), PHP_EOL;

function convert_title_to_utf8($contents)
{
    $dom = str_get_html($contents);
    $title = $dom->find('title', 0);
    if (empty($title)) {
        return null;
    }
    $title = $title->plaintext;
    $metas = $dom->find('meta');
    $charset = 'auto';
    foreach ($metas as $meta) {
        if (!empty($meta->charset)) { // html5
            $charset = $meta->charset;
        } else if (preg_match('@charset=(.+)@', $meta->content, $match)) {
            $charset = $match[1];
        }
    }
    if (!in_array(strtolower($charset), array_map('strtolower', mb_list_encodings()))) {
        $charset = 'auto';
    }
    return mb_convert_encoding($title, 'UTF-8', $charset);
}
Nobu
la source
-1

J'ai eu le même problème avec phpQuery ( ISO-8859-1 au lieu d' UTF-8 ) et ce hack m'a aidé:

$html = '<?xml version="1.0" encoding="UTF-8" ?>' . $html;

mb_internal_encoding('UTF-8'), phpQuery::newDocumentHTML($html, 'utf-8'), mbstring.internal_encodingEt d' autres manipulations ne prennent aucun effet.

user2448995
la source
-1

Essayez sans 'auto'

C'est:

mb_detect_encoding($text)

au lieu de:

mb_detect_encoding($text, 'auto')

Plus d'informations peuvent être trouvées ici: mb_detect_encoding

tkartas
la source