PHP DOMDocument loadHTML codiert UTF-8 nicht richtig

194

Ich versuche, HTML mit DOMDocument zu analysieren, aber wenn ich das tue, verliere ich plötzlich meine Codierung (zumindest erscheint es mir so).

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

Das Ergebnis dieses Codes ist, dass ich eine Reihe von Zeichen bekomme, die nicht japanisch sind. Wenn ich es jedoch tue:

echo $profile;

es wird korrekt angezeigt. Ich habe saveHTML und saveXML ausprobiert und beide werden nicht richtig angezeigt. Ich benutze PHP 5.3.

Was ich sehe:

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

Was sollte gezeigt werden:

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

BEARBEITEN: Ich habe den Code auf fünf Zeilen vereinfacht, damit Sie ihn selbst testen können.

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

Hier ist das HTML, das zurückgegeben wird:

<div lang="ja"><p>イリノイ州シカゴã«ã¦ã€ã‚¢ã‚¤ãƒ«ãƒ©ãƒ³ãƒ‰ç³»ã®å®¶åº­ã«ã€</p></div>
<div lang="ja"><p>イリノイ州シカゴにて、アイルランド系の家庭に、</p></div>
Etwas A.
quelle
Dies kann Ihnen helfen. stackoverflow.com/questions/1580543/…
frustrierte
Vielen Dank. Ich habe all das überprüft und nichts hat geholfen. Ich verstehe nicht ????, aber einen anderen seltsamen Text. Ich werde versuchen, es hier einzufügen, weiß aber nicht, wie die Site es anzeigen wird.
Etwas A.
Versuchen Sie es mit utf8_encode
Webnet
Versucht ohne Erfolg. Hat die gleichen Zeichen wie zuvor zurückgegeben.
Etwas A.

Antworten:

512

DOMDocument::loadHTMLbehandelt Ihre Zeichenfolge als ISO-8859-1, sofern Sie nichts anderes angeben. Dies führt dazu, dass UTF-8-Zeichenfolgen falsch interpretiert werden.

Wenn Ihre Zeichenfolge keine XML-Codierungsdeklaration enthält, können Sie eine voranstellen, damit die Zeichenfolge als UTF-8 behandelt wird:

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

Wenn Sie nicht wissen können, ob die Zeichenfolge bereits eine solche Deklaration enthält, gibt es in SmartDOMDocument eine Problemumgehung, die Ihnen helfen sollte:

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

Dies ist keine gute Problemumgehung, aber da nicht alle Zeichen in ISO-8859-1 (wie diese Katana) dargestellt werden können, ist dies die sicherste Alternative.

cmbuckley
quelle
1
Ja, das hat es geschafft. Danke für Ihre Hilfe. Ich habe saveHTML, saveXML ausprobiert und nicht gedacht, dass das Problem während des Ladens aufgetreten sein könnte.
Etwas A.
4
Der Aufruf mb_convert_encoding hat bei mir funktioniert, während das Voranstellen der Codierungsdeklaration nicht funktioniert hat. Wahrscheinlich, weil das Dokument bereits eine widersprüchliche Erklärung hatte. Vielen Dank - ich habe viel Zeit gespart, um das zu verfolgen.
Peter Bagnall
1
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $content);hat es für mich in PHP7 behoben (es ist also immer noch ein Problem) - dies ist ein wirklich ärgerliches Problem, da ich utf8 im HTML-Dokument (mit <meta charset="UTF-8" />) definiert habe, aber das hat keine Auswirkung, es scheint den <? xml-Teil zu benötigen, der ist völlig unintuitiv.
iquito
11
Noch im Jahr 2017 ist diese Antwort relevant und hat auch für mich funktioniert. Ich hatte meine Datenbank-, Multibyte-, HTML-Meta-Tag- und DOM-Codierung auf utf8 eingestellt und hatte immer noch eine schlechte Codierung beim Importieren des Knotens von einem DOC in einen anderen. php.net/manual/en/function.mb-convert-encoding.php war das Update .
Louis Loudog Trottier
6
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));funktioniert super! Vielen Dank,
vee
66

Das Problem liegt bei saveHTML()und saveXML()beide funktionieren unter Unix nicht richtig. Sie speichern UTF-8-Zeichen unter Unix nicht korrekt, funktionieren jedoch unter Windows.

Die Problemumgehung ist sehr einfach:

Wenn Sie die Standardeinstellung ausprobieren, wird der von Ihnen beschriebene Fehler angezeigt

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

Sie müssen lediglich wie folgt speichern:

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

In dieser Codezeile werden Ihre UTF-8-Zeichen korrekt gespeichert. Verwenden Sie dieselbe Problemumgehung, wenn Sie verwenden saveXML().


Aktualisieren

Wie von " Jack M " im Kommentarbereich unten vorgeschlagen und von " Pamela " und " Marco Aurélio Deleu " verifiziert , könnte die folgende Variante in Ihrem Fall funktionieren:

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

Hinweis

  1. Englische Zeichen verursachen keine Probleme, wenn Sie sie saveHTML()ohne Parameter verwenden (da englische Zeichen in UTF-8 als Einzelbytezeichen gespeichert werden).

  2. Das Problem tritt auf, wenn Sie Multibyte-Zeichen haben (z. B. Chinesisch, Russisch, Arabisch, Hebräisch usw.).

Ich empfehle diesen Artikel zu lesen: http://coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/ . Sie werden verstehen, wie UTF-8 funktioniert und warum Sie dieses Problem haben. Es dauert ungefähr 30 Minuten, aber es ist gut investierte Zeit.

Greeso
quelle
5
Ich musste utf8_decode verwenden, während ich diese Lösung verwendete. Vielen Dank!
Jack M.
9
Dies musste utf8_decode ($ dom-> saveHTML (dom-> documentElement)) werden, um meine Sonderzeichen zu erhalten. Ansonsten wurden sie einfach etwas anderes. Nur erwähnen, falls es jemand anderem hilft.
Jack M.
4
Danke @MrJack. Ich musste das auch tun, damit es ohne die seltsamen Charaktere angezeigt wurde$str = utf8_decode($dom->saveHTML($dom->documentElement));
Pamela
1
utf8_decode($dom->saveHTML($dom->documentElement));habe es perfekt für mich gemacht.
Marco Aurélio Deleu
2
Damit hast du mir das Leben gerettet. Ich habe ÜBERALL nach dieser Antwort gesucht! Danke dir!
Paulo Hgo
15

Stellen Sie sicher, dass die echte Quelldatei als UTF-8 gespeichert ist (Sie können sogar die nicht empfohlenen Stücklistenzeichen mit UTF-8 ausprobieren, um dies sicherzustellen).

Stellen Sie auch bei HTML sicher, dass Sie die richtige Codierung mithilfe von metaTags deklariert haben :

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

Wenn es sich um ein CMS handelt (da Sie Ihre Frage mit Joomla markiert haben), müssen Sie möglicherweise die entsprechenden Einstellungen für die Codierung konfigurieren.

Hossein
quelle
Ich verstehe, was Sie sagen, aber ich habe keine Probleme, die Zeichen anzuzeigen. wenn ich "echo $ profile" mache; es funktioniert gut. Wenn das DomDocument davon erfährt, schlägt es fehl.
Etwas A.
2
Ihr Meta verhindert, dass saveHTML alles über ASCII in Entitäten codiert. Die Lösung, nach der ich gesucht habe :)
Sod
2
<meta charset="UTF-8">Nebenbei bemerkt, das neuere Tag funktioniert nicht mit DOMDocument.
Taylan
10

Sie können einer Zeile, die die utf-8Codierung erzwingt, Folgendes voranstellen :

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

Und Sie können dann mit dem Code fortfahren, den Sie bereits haben, wie:

$doc->saveXML()
Ivan
quelle
10

Ich habe eine Weile gebraucht, um das herauszufinden, aber hier ist meine Antwort.

Bevor ich DomDocument verwende, verwende ich file_get_contents, um URLs abzurufen und sie dann mit Zeichenfolgenfunktionen zu verarbeiten. Vielleicht nicht der beste Weg, aber schnell. Nachdem ich überzeugt war, dass Dom genauso schnell war, versuchte ich zuerst Folgendes:

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

Dies scheiterte spektakulär daran, die UTF-8-Codierung trotz der richtigen Meta-Tags, PHP-Einstellungen und aller anderen hier und anderswo angebotenen Mittel beizubehalten. Folgendes funktioniert:

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

usw. Jetzt stimmt alles mit der Welt. Hoffe das hilft.

Sam
quelle
Ich wollte meiner obigen Antwort nur hinzufügen, dass eine andere Möglichkeit, dies zu beheben, die folgende ist, die auch an anderer Stelle vorgeschlagen wird: if ($ dom-> loadHTML ('<? Xml encoding = "UTF-8">'. $ Str) = = falsch). Nachdem ich meine Antwort gepostet hatte, fand ich eine Gelegenheit, bei der mein erster Vorschlag fehlschlug, der zweite jedoch funktionierte.
Sam
Funktioniert bei mir auch ohne die params in DomDocument('1.0', 'UTF-8'). In meinem Fall wird aber nur teilweise HTML geladen.
JKB
5

Sie müssen dem DOMDocument eine Version Ihres HTML-Codes mit einem sinnvollen Header zuführen. Genau wie HTML5.

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

Vielleicht ist es eine gute Idee, Ihr HTML so gültig wie möglich zu halten, damit Sie nicht in Probleme geraten, wenn Sie mit der Abfrage beginnen ... herum :-) und sich von htmlentities!!!! fernhalten !!!! Das ist ein notwendiges Hin und Her, um Ressourcen zu verschwenden. Halte deinen Code verrückt !!!!

Lazaros Kosmidis
quelle
5

Ich benutze PHP 7.3.8 auf einem Manjaro und habe mit persischen Inhalten gearbeitet. Dies löste mein Problem:

$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;
sajed Zarrinpour
quelle
Genau diesen Rat gab Sam Jahre zuvor auf derselben Seite. Bitte machen Sie keine redundanten Informationen.
Mickmackusa
4

Werke finden für mich:

$dom = new \DOMDocument;
$dom->loadHTML(utf8_decode($html));
...
return  utf8_encode( $dom->saveHTML());
mMo
quelle
2
Seien Sie vorsichtig, utf8_decode kann Informationen verlieren (ersetzt durch a ?)
jwal
2

Verwenden Sie es für das richtige Ergebnis

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

Diese Operation

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

Es ist ein schlechter Weg, weil spezielle Symbole wie & lt; , & gt; kann sich in $ profile befinden und wird nach mb_convert_encoding nicht zweimal konvertiert. Es ist das Loch für XSS und falsches HTML.

Alexander Goncharov
quelle
1

Das einzige, was für mich funktioniert hat, war die akzeptierte Antwort von

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

JEDOCH

Dies brachte neue Probleme mit sich, die <?xml encoding="utf-8" ?>in der Ausgabe des Dokuments enthalten waren.

Die Lösung für mich war dann zu tun

foreach ($doc->childNodes as $xx) {
    if ($xx instanceof \DOMProcessingInstruction) {
        $xx->parentNode->removeChild($xx);
    }
}

Einige Lösungen sagten mir xml, dass ich ausführen musste, um den Header zu entfernen

$dom->saveXML($dom->documentElement);

Dies funktionierte bei mir nicht wie bei einem Teildokument (z. B. einem Dokument mit zwei <p>Tags), nur eines der <p>Tags wurde zurückgegeben.

Luke Madhanga
quelle
0

Das Problem ist, dass Sie die Codierung verlieren, wenn Sie der Funktion DOMDocument :: saveHTML () Parameter hinzufügen. In einigen Fällen müssen Sie die Verwendung des Parameters vermeiden und die alte Zeichenfolgenfunktion verwenden, um zu finden, wonach Sie suchen.

Ich denke, die vorherige Antwort funktioniert für Sie, aber da diese Problemumgehung für mich nicht funktioniert hat, füge ich diese Antwort hinzu, um Personen zu helfen, die möglicherweise in meinem Fall sind.

copndz
quelle
0

Kann auch wie unten codiert werden .... gesammelt von https://davidwalsh.name/domdocument-utf8-problem

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