PHP DOMDocument loadHTML ไม่ได้เข้ารหัส UTF-8 อย่างถูกต้อง


195

ฉันพยายามแยก HTML บางส่วนโดยใช้ DOMDocument แต่เมื่อฉันทำฉันก็สูญเสียการเข้ารหัสของฉันทันที

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

ผลลัพธ์ของรหัสนี้คือฉันได้รับตัวละครมากมายที่ไม่ใช่ภาษาญี่ปุ่น อย่างไรก็ตามถ้าฉัน:

echo $profile;

มันแสดงอย่างถูกต้อง ฉันลอง saveHTML และ saveXML แล้วและไม่แสดงอย่างถูกต้อง ฉันใช้ PHP 5.3

ฉันเห็นอะไร:

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

สิ่งที่ควรแสดง:

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

แก้ไข: ฉันลดความซับซ้อนของรหัสลงไปที่ห้าบรรทัดเพื่อให้คุณสามารถทดสอบด้วยตัวเอง

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

นี่คือ html ที่ส่งคืน:

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

สิ่งนี้อาจช่วยคุณได้ stackoverflow.com/questions/1580543/…
frustratedtech

ขอบคุณ ฉันตรวจสอบทั้งหมดและไม่มีอะไรช่วย ฉันไม่ได้รับ ???? แต่มีข้อความแปลก ๆ ฉันจะลองวางที่นี่ แต่ไม่รู้ว่าไซต์จะแสดงยังไง
A.

ลองใช้utf8_encode
Webnet

พยายามไม่ประสบความสำเร็จ ส่งคืนอักขระเดิมเหมือนเดิม
ก.

คำตอบ:


515

DOMDocument::loadHTMLจะถือว่าสตริงของคุณอยู่ใน ISO-8859-1 เว้นแต่คุณจะระบุเป็นอย่างอื่น ซึ่งส่งผลให้มีการตีความสตริง UTF-8 ไม่ถูกต้อง

หากสตริงของคุณไม่มีการประกาศการเข้ารหัส XML คุณสามารถเพิ่มสตริงเพื่อทำให้สตริงถูกใช้เป็น UTF-8:

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

หากคุณไม่สามารถรู้ได้ว่าสตริงนั้นจะมีการประกาศดังกล่าวอยู่หรือไม่มีวิธีแก้ปัญหาในSmartDOMDocumentซึ่งจะช่วยคุณได้:

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

นี่ไม่ใช่วิธีแก้ปัญหาที่ยอดเยี่ยม แต่เนื่องจากตัวละครบางตัวไม่สามารถแสดงใน ISO-8859-1 (เช่นคาตานะเหล่านี้) จึงเป็นทางเลือกที่ปลอดภัยที่สุด


1
ใช่มันทำ ขอขอบคุณสำหรับความช่วยเหลือของคุณ. ฉันลอง saveHTML, saveXML ไม่คิดว่าปัญหาอาจเกิดขึ้นระหว่างการโหลด
A.

4
การเรียก mb_convert_encoding ใช้งานได้สำหรับฉันในขณะที่การประกาศการเข้ารหัสไม่ได้เตรียมไว้ อาจเป็นเพราะเอกสารมีการประกาศที่ขัดแย้งกันอยู่แล้ว ขอบคุณมาก - ช่วยฉันประหยัดเวลาได้มากในการไล่ล่าสิ่งนี้
Peter Bagnall

1
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $content);แก้ไขให้ฉันใน PHP7 (ดังนั้นจึงยังคงเป็นปัญหา) - นี่เป็นปัญหาที่น่ารำคาญจริงๆเพราะฉันกำหนด utf8 ในเอกสาร HTML (ด้วย<meta charset="UTF-8" />) แต่ที่ไม่มีผลดูเหมือนว่าต้องการส่วน <? xml ซึ่ง ใช้งานง่ายโดยสิ้นเชิง
iquito

11
ยังอยู่ในปี 2560 คำตอบนี้มีความเกี่ยวข้องและทำงานให้ฉันด้วย ฉันมีฐานข้อมูลหลายไบต์เมตาแท็ก html และการเข้ารหัส DOM ทั้งหมดตั้งค่าเป็น utf8 และยังมีการเข้ารหัสที่ไม่ดีในการนำเข้าโหนดจาก DOC หนึ่งไปยังอีก php.net/manual/th/function.mb-convert-encoding.phpคือการแก้ไข
Louis Loudog Trottier

6
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));ใช้งานได้ดี! ขอบคุณ
ve

67

ปัญหาเกิดขึ้นกับsaveHTML()และsaveXML()ทั้งคู่ทำงานไม่ถูกต้องใน Unix ไม่สามารถบันทึกอักขระ UTF-8 ได้อย่างถูกต้องเมื่อใช้ใน Unix แต่ทำงานได้ใน Windows

วิธีแก้ปัญหาง่ายมาก:

หากคุณลองใช้ค่าเริ่มต้นคุณจะได้รับข้อผิดพลาดที่คุณอธิบาย

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

สิ่งที่คุณต้องทำคือบันทึกดังต่อไปนี้:

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

บรรทัดของรหัสนี้จะได้รับการบันทึกอักขระ UTF-8 ของคุณอย่างถูกต้อง saveXML()ใช้วิธีแก้ปัญหาแบบเดียวกันถ้าคุณกำลังใช้


ปรับปรุง

ตามที่แนะนำโดย " Jack M " ในส่วนความคิดเห็นด้านล่างและตรวจสอบโดย " Pamela " และ " Marco Aurélio Deleu " รูปแบบต่อไปนี้อาจใช้ได้ในกรณีของคุณ:

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

บันทึก

  1. อักขระภาษาอังกฤษไม่ทำให้เกิดปัญหาใด ๆ เมื่อคุณใช้saveHTML()โดยไม่มีพารามิเตอร์ (เนื่องจากบันทึกอักขระภาษาอังกฤษเป็นอักขระไบต์เดียวใน UTF-8)

  2. ปัญหาเกิดขึ้นเมื่อคุณมีอักขระหลายไบต์ (เช่นจีน, รัสเซีย, อาหรับ, ฮิบรู, ... ฯลฯ )

ผมขอแนะนำให้อ่านบทความนี้: http://coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/ คุณจะเข้าใจว่า UTF-8 ทำงานอย่างไรและทำไมคุณถึงมีปัญหานี้ จะใช้เวลาประมาณ 30 นาที แต่ใช้เวลาพอสมควร


5
ฉันต้อง utf8_decode ขณะใช้โซลูชันนี้ ขอบคุณ!
Jack M.

9
สิ่งนี้ต้องกลายเป็น utf8_decode ($ dom-> saveHTML (dom-> documentElement)) เพื่อรักษาอักขระพิเศษของฉัน มิฉะนั้นพวกเขากลายเป็นอย่างอื่น เพียงกล่าวถึงในกรณีที่ช่วยคนอื่น
Jack M.

4
ขอบคุณ @MrJack ฉันต้องทำเช่นเดียวกันเพื่อให้มันแสดงโดยไม่มีตัวละครแปลก ๆ$str = utf8_decode($dom->saveHTML($dom->documentElement));
Pamela

1
utf8_decode($dom->saveHTML($dom->documentElement));ทำอย่างสมบูรณ์แบบสำหรับฉัน
Marco Aurélio Deleu

2
คุณช่วยชีวิตฉันด้วยสิ่งนี้ ฉันมองหาคำตอบนี้ทุกที่! ขอบคุณ!
Paulo Hgo

15

ตรวจสอบให้แน่ใจว่าได้บันทึกไฟล์ต้นฉบับจริงเป็น UTF-8 (คุณอาจต้องการลอง BOM Chars ที่ไม่แนะนำด้วย UTF-8 เพื่อให้แน่ใจ)

นอกจากนี้ในกรณีของ HTML ตรวจสอบให้แน่ใจว่าคุณได้ประกาศการเข้ารหัสที่ถูกต้องโดยใช้metaแท็ก:

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

หากเป็น CMS (ตามที่คุณติดแท็กคำถามของคุณด้วย Joomla) คุณอาจต้องกำหนดการตั้งค่าที่เหมาะสมสำหรับการเข้ารหัส


ฉันเข้าใจสิ่งที่คุณพูด แต่ฉันก็ไม่มีปัญหาในการแสดงตัวละคร ถ้าฉันทำ "echo $ profile;" มันใช้งานได้ดี มันคือเมื่อ DomDocument ได้รับการดูแลจากมันว่ามันเริ่มต้นล้มเหลว
ก.

2
เมตาของคุณป้องกัน saveHTML จากการเข้ารหัสทุกอย่างเหนือ ASCII ไปเป็นเอนทิตี วิธีแก้ปัญหาที่ฉันกำลังมองหา :)
sod

2
ในฐานะที่เป็นบันทึกย่อด้าน<meta charset="UTF-8">แท็กใหม่จะไม่ทำงานกับ DOMDocument
Taylan

10

คุณสามารถขึ้นต้นบรรทัดบังคับให้utf-8เข้ารหัสเช่นนี้

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

และคุณสามารถดำเนินการต่อกับรหัสที่คุณมีอยู่แล้วเช่น:

$doc->saveXML()

10

นี่ใช้เวลาซักพักนึง แต่นี่คือคำตอบ

ก่อนที่จะใช้ DomDocument ฉันจะใช้ file_get_contents เพื่อดึง URL แล้วประมวลผลด้วยฟังก์ชันสตริง อาจไม่ใช่วิธีที่ดีที่สุด แต่อย่างรวดเร็ว หลังจากได้รับความเชื่อมั่นจาก Dom แล้วฉันก็ลองต่อไปนี้เป็นครั้งแรก:

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

สิ่งนี้ล้มเหลวอย่างน่าทึ่งในการรักษาการเข้ารหัส UTF-8 แม้จะมีเมตาแท็กที่เหมาะสมการตั้งค่า php และการเยียวยาที่เหลือทั้งหมดที่มีให้ที่นี่และที่อื่น ๆ นี่คือสิ่งที่ใช้งานได้:

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

ฯลฯ ตอนนี้ทุกอย่างถูกต้องกับโลก หวังว่านี่จะช่วยได้


แค่ต้องการเพิ่มคำตอบของฉันไว้ด้านบนอีกวิธีหนึ่งในการแก้ไขปัญหานี้คือการแนะนำต่อไปนี้ที่อื่นเช่น: if ($ dom-> loadHTML ('<? xml encoding = "UTF-8">'. $ str) = = false) หลังจากโพสต์คำตอบของฉันฉันพบโอกาสที่ข้อเสนอแนะครั้งแรกของฉันล้มเหลว แต่ข้อที่สองใช้งานได้
Sam

ได้ผลสำหรับฉันแม้ไม่มี params DomDocument('1.0', 'UTF-8')ค่ะ แต่ในกรณีของฉันโหลด html เพียงบางส่วนเท่านั้น
JKB

5

คุณต้องป้อน DOMDocument เวอร์ชันของ HTML ด้วยส่วนหัวที่เหมาะสม เช่นเดียวกับ HTML5

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

อาจเป็นความคิดที่ดีที่จะรักษา html ของคุณให้ถูกต้องเท่าที่คุณจะทำได้ดังนั้นคุณจะไม่เกิดปัญหาเมื่อคุณเริ่มสืบค้น ... รอบ ๆ :-) และอยู่ห่างจากhtmlentities!!!! นั่นเป็นทรัพยากรที่สูญเปล่าไปมาที่จำเป็น รักษารหัสของคุณเสียสติ !!!!


5

ฉันใช้ php 7.3.8 กับ manjaro และฉันทำงานกับเนื้อหาเปอร์เซีย วิธีนี้แก้ไขปัญหาของฉัน:

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

คำแนะนำแบบเดียวกันนี้ให้โดย Sam ปีก่อนหน้านี้ในหน้าเดียวกัน กรุณาอย่าโพสต์ข้อมูลที่ซ้ำซ้อน
mickmackusa

4

ทำงาน finde สำหรับฉัน:

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

2
โปรดระวัง utf8_decode อาจทำให้ข้อมูลสูญหาย (แทนที่ด้วย a ?)
jwal

2

ใช้เพื่อผลลัพธ์ที่ถูกต้อง

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

การดำเนินการนี้

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

มันเป็นวิธีที่ไม่ดีเพราะสัญลักษณ์พิเศษเช่น & lt; , & gt; สามารถอยู่ในโปรไฟล์ $ และพวกเขาจะไม่แปลงสองครั้งหลังจาก mb_convert_encoding มันเป็นช่องโหว่สำหรับ XSS และ HTML ที่ไม่ถูกต้อง


1

สิ่งเดียวที่ใช้ได้ผลสำหรับฉันคือคำตอบที่ได้รับการยอมรับ

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

อย่างไรก็ตาม

สิ่งนี้นำมาซึ่งปัญหาใหม่เกี่ยวกับการมี<?xml encoding="utf-8" ?>อยู่ในผลลัพธ์ของเอกสาร

วิธีแก้ปัญหาสำหรับฉันคือทำแล้ว

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

วิธีแก้ปัญหาบางอย่างบอกฉันว่าต้องลบxmlส่วนหัวซึ่งฉันต้องทำ

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

สิ่งนี้ไม่ได้ผลสำหรับฉันสำหรับเอกสารบางส่วน (เช่นเอกสารที่มีสอง<p>แท็ก) เพียงหนึ่ง<p>แท็กที่จะถูกส่งคืน


0

ปัญหาคือเมื่อคุณเพิ่มพารามิเตอร์ในฟังก์ชัน DOMDocument :: saveHTML () คุณจะสูญเสียการเข้ารหัส ในบางกรณีคุณจะต้องหลีกเลี่ยงการใช้พารามิเตอร์และใช้ฟังก์ชันสตริงเก่าเพื่อค้นหาสิ่งที่คุณต้องการ

ฉันคิดว่าคำตอบก่อนหน้านี้เหมาะกับคุณ แต่เนื่องจากวิธีแก้ปัญหานี้ไม่ได้ผลสำหรับฉันฉันจึงเพิ่มคำตอบนั้นเพื่อช่วย ppl ที่อาจเป็นในกรณีของฉัน


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.