web-dev-qa-db-fra.com

PHP DOMDocument loadHTML ne codant pas correctement UTF-8

J'essaie d'analyser du code HTML à l'aide de DOMDocument, mais lorsque je le fais, je perds soudainement mon codage (du moins, c'est ainsi qu'il me semble).

$profile = "<div><p>various japanese characters</p></div>";
$dom = new DOMDocument();
$dom->loadHTML($profile); 

$divs = $dom->getElementsByTagName('div');

foreach ($divs as $div) {
    echo $dom->saveHTML($div);
}

Le résultat de ce code est que je reçois un tas de caractères qui ne sont pas japonais. Cependant, si je le fais:

echo $profile;

il affiche correctement. J'ai essayé saveHTML et saveXML, et aucun ne s'affiche correctement. J'utilise PHP 5.3.

Ce que je vois:

ã¤ãªãã¤å·ã·ã«ã´ã«ã¦ãã¢ã¤ã«ã©ã³ãç³»ã®å®¶åº­ã«ã9人åå¼ã®5çªç®ã¨ãã¦çã¾ãããå½¼ãå«ãã¦4人ã俳åªã«ãªã£ããç¶è¦ªã¯æ¨æã®ã»ã¼ã«ã¹ãã³ã§ãæ¯è¦ªã¯éµä¾¿å±ã®å®¢å®¤ä¿ã ã£ããé«æ ¡æ代ã¯ã­ã£ãã£ã®ã¢ã«ãã¤ãã«å¤ãã¿ãæè²è³éãåããªããã«ããªãã¯ç³»ã®é«æ ¡ã¸é²å­¦ã

Ce qui devrait être montré:

イリノイ州シカゴにて、アイルランド系の家庭に、9人兄弟の5番目として生まれる。彼を含めて4人が俳優になった。父親は木材のセールスマンで、母親は郵便局の客室係だった。高校時代はキャディのアルバイトに勤しみ、教育資金を受けながらカトリック系の高校へ進学

EDIT: J'ai simplifié le code à cinq lignes afin que vous puissiez le tester vous-même.

$profile = "<div lang=ja><p>イリノイ州シカゴにて、アイルランド系の家庭に、</p></div>";
$dom = new DOMDocument();
$dom->loadHTML($profile);
echo $dom->saveHTML();
echo $profile;

Voici le code HTML qui est renvoyé:

<div lang="ja"><p>イリノイ州シカゴã«ã¦ã€ã‚¢ã‚¤ãƒ«ãƒ©ãƒ³ãƒ‰ç³»ã®å®¶åº­ã«ã€</p></div>
<div lang="ja"><p>イリノイ州シカゴにて、アイルランド系の家庭に、</p></div>
169
Slightly A.

DOMDocument::loadHTML considérera votre chaîne comme étant dans ISO-8859-1, sauf indication contraire de votre part. Cela se traduit par une interprétation incorrecte des chaînes UTF-8.

Si votre chaîne ne contient pas de déclaration de codage XML, vous pouvez en ajouter une au début pour que la chaîne soit traitée comme UTF-8:

$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $profile);
echo $dom->saveHTML();

Si vous ne pouvez pas savoir si la chaîne contiendra déjà une telle déclaration, il existe une solution de contournement dans SmartDOMDocument qui devrait vous aider:

$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));
echo $dom->saveHTML();

La solution de contournement n'est pas géniale, mais comme tous les caractères ne peuvent pas être représentés dans ISO-8859-1 (comme ces katana), c'est l'alternative la plus sûre.

443
cmbuckley

Le problème est avec saveHTML() et saveXML(), les deux ne fonctionnent pas correctement sous Unix. Ils n'enregistrent pas correctement les caractères UTF-8 lorsqu'ils sont utilisés sous Unix, mais ils fonctionnent sous Windows.

La solution de contournement est très simple:

Si vous essayez la valeur par défaut, vous obtiendrez l'erreur que vous avez décrite

$str = $dom->saveHTML(); // saves incorrectly

Tout ce que vous avez à faire est de sauvegarder comme suit:

$str = $dom->saveHTML($dom->documentElement); // saves correctly

Cette ligne de code permettra d’enregistrer correctement vos caractères UTF-8. Utilisez la même solution de contournement si vous utilisez saveXML().


Mettre à jour

Comme suggéré par " Jack M " dans la section commentaires ci-dessous, et vérifié par " Pamela " et " Marco Aurélio Dele ", la variante suivante pourrait fonctionner dans votre cas:

$str = utf8_decode($dom->saveHTML($dom->documentElement));

Remarque

  1. Les caractères anglais ne posent aucun problème lorsque vous utilisez saveHTML() sans paramètres (car les caractères anglais sont enregistrés en tant que caractères à un octet dans UTF-8)

  2. Le problème survient lorsque vous avez des caractères multi-octets (tels que le chinois, le russe, l'arabe, l'hébreu, etc.).

Je vous recommande de lire cet article: http://coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/ . Vous comprendrez comment fonctionne UTF-8 et pourquoi vous avez ce problème. Cela vous prendra environ 30 minutes, mais c'est du temps bien dépensé.

54
Greeso

Assurez-vous que le fichier source réel est enregistré au format UTF-8 (vous pouvez même essayer les caractères de nomenclature non recommandés avec UTF-8 pour vous en assurer).

Toujours dans le cas du code HTML, assurez-vous d’avoir déclaré le codage correct à l’aide des balises meta:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

S'il s'agit d'un CMS (puisque vous avez balisé votre question avec Joomla), vous devrez peut-être configurer les paramètres appropriés pour le codage.

14
Hossein

Vous pouvez préfixer une ligne imposant le codage utf-8, comme ceci:

@$doc->loadHTML('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $profile);

Et vous pouvez ensuite continuer avec le code que vous avez déjà, comme:

$doc->saveXML()
10
Ivan

Cela m'a pris un certain temps à comprendre, mais voici ma réponse.

Avant d’utiliser DomDocument, j’utilisais file_get_contents pour récupérer des URL, puis les traiter avec des fonctions de chaîne. Peut-être pas le meilleur moyen mais rapide. Après avoir été convaincu que Dom était aussi rapide, j'ai d'abord essayé ce qui suit:

$dom = new DomDocument('1.0', 'UTF-8');
if ($dom->loadHTMLFile($url) == false) { // read the url
    // error message
}
else {
    // process
}

Cela a échoué de façon spectaculaire en préservant le codage UTF-8 malgré les balises méta, les paramètres php et tous les autres remèdes proposés ici et ailleurs. Voici ce qui fonctionne:

$dom = new DomDocument('1.0', 'UTF-8');
$str = file_get_contents($url);
if ($dom->loadHTML(mb_convert_encoding($str, 'HTML-ENTITIES', 'UTF-8')) == false) {
}

etc. Maintenant tout va bien avec le monde. J'espère que cela t'aides.

7
Sam

Vous devez fournir au DOMDocument une version de votre code HTML avec un en-tête qui a du sens. Tout comme HTML5.

$profile ='<?xml version="1.0" encoding="'.$_encoding.'"?>'. $html;

c'est peut-être une bonne idée de garder votre code HTML aussi valide que possible, afin d'éviter les problèmes lorsque vous commencerez à interroger ... autour de :-) et restez à l'écart de htmlentities !!!! C'est un gaspillage de ressources nécessaire. gardez votre code fou !!!!

5
Lazaros Kosmidis

Fonctionne pour moi:

$dom = new \DOMDocument;
$dom->loadHTML(utf8_decode($html));
...
return  utf8_encode( $dom->saveHTML());
4
mMo

Utilisez-le pour un résultat correct

$dom = new DOMDocument();
$dom->loadHTML('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . $profile);
echo $dom->saveHTML();
echo $profile;

Cette opération

mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8');

C'est une mauvaise façon, car des symboles spéciaux tels que & lt; , & gt; peuvent être dans le profil $, et ils ne convertiront pas deux fois après mb_convert_encoding. C'est le trou pour XSS et HTML incorrect.

1

J'utilise PHP 7.3.8 sur un manjaro et je travaillais avec du contenu persan. This résolu mon problème:

$html = 'hi</b><p>سلام<div>の家庭に、9 ☆';
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
print $doc->saveHTML($doc->documentElement) . PHP_EOL . PHP_EOL;
1
sajed zarrinpour

Le problème est que lorsque vous ajoutez un paramètre à la fonction DOMDocument :: saveHTML (), vous perdez le codage. Dans quelques cas, vous devrez éviter l'utilisation du paramètre et utiliser l'ancienne fonction de chaîne pour trouver ce que vous recherchez.

Je pense que la réponse précédente fonctionne pour vous, mais puisque cette solution de contournement n'a pas fonctionné pour moi, j'ajoute cette réponse pour aider les personnes qui pourraient être dans mon cas.

0
copndz