PHP DOMDocument loadHTML, UTF-8'i doğru kodlamıyor


195

Ben DOMDocument kullanarak bazı HTML ayrıştırmak için çalışıyorum, ama bunu yaptığımda aniden kodumu (en azından bana böyle görünüyor) kaybederim.

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

Bu kod sonucu Japonca olmayan bir sürü karakter elde olmasıdır. Ancak, eğer:

echo $profile;

doğru bir şekilde görüntülenir. SaveHTML ve saveXML'yi denedim ve ikisi de doğru görüntülenmedi. PHP 5.3 kullanıyorum.

Ne görüyorum:

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

Ne gösterilmelidir:

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

EDIT: Kodu kendiniz test edebilmek için beş satıra kadar basitleştirdim.

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

İşte döndürülen html:

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


Teşekkürler. Tüm bunları kontrol ettim ve hiçbir şey yardımcı olmadı. Anlamadım, ama başka garip bir metin. Buraya yapıştırmayı deneyeceğim, ancak sitenin nasıl göstereceğini bilmiyorum.
Biraz A.


Başarısız denedi. Öncekiyle aynı karakterleri döndürdü.
Biraz A.

Yanıtlar:


515

DOMDocument::loadHTMLaksi belirtilmedikçe dizenizi ISO-8859-1'deki gibi ele alacaktır. Bu UTF-8 dizelerinin yanlış yorumlanmasına neden olur.

Dizeniz bir XML kodlama bildirimi içermiyorsa, dizenin UTF-8 olarak değerlendirilmesini sağlamak için bir tane ekleyebilirsiniz.

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

Dizenin zaten böyle bir bildirim içerip içermeyeceğini bilmiyorsanız, SmartDOMDocument'te size yardımcı olacak bir geçici çözüm vardır :

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

Bu harika bir çözüm değil, ancak tüm karakterler ISO-8859-1'de (bu katana gibi) temsil edilemediğinden, en güvenli alternatif.


1
Evet, başardı. Yardımın için teşekkürler. SaveHTML'yi kurtardım, saveXML, sorunun yükleme sırasında gelmekte olduğunu düşünmüyordum.
Biraz A.

4
Mb_convert_encoding çağrısı benim için çalıştı, oysa kodlama bildirimini eklemek için çalışmadı. Muhtemelen belgenin çelişkili bir beyanı olduğu için. Çok teşekkürler - bunu kovalamak için çok zaman kazandım.
Peter Bagnall

1
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $content);PHP7 benim için düzeltildi (bu yüzden hala bir sorun) - bu gerçekten sinir bozucu bir sorun, çünkü HTML belgesinde utf8 tanımladı (ile <meta charset="UTF-8" />) ama hiçbir etkisi yok, <? xml bölümüne ihtiyaç duyuyor gibi görünüyor, hangi tamamen kasıtsızdır.
iquito

11
Yine de 2017'de bu cevap alakalı ve benim için çalıştı. Benim veritabanı, multibyte, html meta etiketi ve DOM kodlama tüm utf8 için ayarlanmış ve hala bir DOC diğerine düğüm alma kötü kodlama vardı. Düzeltme php.net/manual/tr/function.mb-convert-encoding.php oldu.
Louis Loudog Trottier

6
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));harika çalışıyor! Teşekkür ederim,
vee

67

Sorunun nedeni saveHTML()ve saveXML()her ikisi de Unix düzgün çalışmaz. Unix'te kullanıldığında UTF-8 karakterlerini doğru kaydetmezler, ancak Windows'ta çalışırlar.

Çözüm çok basit:

Varsayılanı denerseniz, açıkladığınız hatayı alırsınız

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

Tek yapmanız gereken aşağıdaki gibi kaydetmek:

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

Bu kod satırı, UTF-8 karakterlerinizin doğru şekilde kaydedilmesini sağlar. Kullanıyorsanız aynı çözümü kullanın saveXML().


Güncelleme

Aşağıdaki yorumlar bölümünde " Jack M " tarafından önerildiği ve " Pamela " ve " Marco Aurélio Deleu " tarafından doğrulandığı gibi, aşağıdaki varyasyon sizin durumunuzda işe yarayabilir:

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

Not

  1. saveHTML()Parametresiz kullandığınızda İngilizce karakterler herhangi bir soruna neden olmaz (çünkü İngilizce karakterler UTF-8'de tek bayt karakterler olarak kaydedilir)

  2. Sorun, çok baytlı karakterleriniz (Çince, Rusça, Arapça, İbranice, vb.)

Bu makaleyi okumanızı tavsiye ederim: http://coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/ . UTF-8'in nasıl çalıştığını ve neden bu sorunu yaşadığınızı anlayacaksınız. Yaklaşık 30 dakika sürecek, ancak iyi harcanan zaman.


5
Bu çözümü kullanırken utf8_decode gerekiyordu. Teşekkürler!
Jack M.

9
Özel karakterlerimi korumak için bunun utf8_decode ($ dom-> saveHTML (dom-> documentElement)) olması gerekiyordu. Aksi takdirde, başka bir şey oldular. Başka birine yardım etmesi durumunda bahsetmek.
Jack

4
Teşekkürler @MrJack. Aynı şeyi tuhaf karakterler olmadan göstermek için de yapmam gerekiyordu$str = utf8_decode($dom->saveHTML($dom->documentElement));
Pamela

1
utf8_decode($dom->saveHTML($dom->documentElement));benim için mükemmel yaptı.
Marco Aurélio Deleu

2
Bununla hayatımı kurtardın. Bu cevabı HER YERDE aradım! Teşekkür ederim!
Paulo Hgo

15

Gerçek kaynak dosyanın UTF-8 olarak kaydedildiğinden emin olun (Emin olmak için tavsiye edilmeyen BOM Listelerini UTF-8 ile denemek isteyebilirsiniz).

Ayrıca HTML durumunda, metaetiketleri kullanarak doğru kodlamayı bildirdiğinizden emin olun :

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

Bu bir CMS ise (sorunuzu Joomla ile etiketlediğiniz gibi) kodlama için uygun ayarları yapılandırmanız gerekebilir.


Ne dediğini anlıyorum, ama karakterleri görüntülemekte sorun yaşamıyorum. "echo $ profile;" iyi çalışıyor. DomDocument onu ele geçirdiğinde başarısız olmaya başlar.
Biraz A.

2
Meta dosyanız saveHTML'nin ASCII üzerindeki her şeyi varlıklara kodlamasını önler. Aradığım çözüm :)
sod

2
Bir yan not olarak, yeni <meta charset="UTF-8">etiket DOMDocument ile çalışmaz.
Taylan

10

Şunun utf-8gibi bir satır uygulayan kodlamanın önekini ekleyebilirsiniz :

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

Daha sonra, sahip olduğunuz kodla devam edebilirsiniz, örneğin:

$doc->saveXML()

10

Anlamak biraz zaman aldı ama işte cevabım.

DomDocument'i kullanmadan önce, url'leri almak ve sonra dize işlevleriyle işlemek için file_get_contents kullanacağım. Belki de en iyi yol değil ama hızlı. Dom'un o kadar hızlı olduğuna ikna olduktan sonra ilk olarak aşağıdakileri denedim:

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

Bu, uygun meta etiketlere, php ayarlarına ve burada ve başka yerlerde sunulan tüm çözümlere rağmen UTF-8 kodlamasını korumada muhteşem bir şekilde başarısız oldu. İşte işe yarayan:

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

Şimdi dünya ile ilgili herşey yolunda. Bu yardımcı olur umarım.


Sadece yukarıdaki yanıtı başka bir yerde de önerilen, bu ele ile başka bir yol olduğunu eklemek istedim: if ($ dom-> loadHTML ('<? Xml encoding = "UTF-8">'. $ Str) = = yanlış). Cevabımı gönderdikten sonra ilk önerimin başarısız olduğu ancak ikincisinin çalıştığı bir fırsat buldum.
Sam

Paramlar olmadan bile benim için çalışıyor DomDocument('1.0', 'UTF-8'). Ama benim durumumda sadece kısmi html yüklü.
JKB

5

DOMDocument'i HTML'nizin bir sürümünü anlamlı bir başlıkla beslemelisiniz. Tıpkı HTML5 gibi.

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

belki html'nizi olabildiğince geçerli tutmak için iyi bir fikirdir, bu yüzden sorgu başlattığınızda ... etrafta :-) ve uzak durduğunuzda sorunlara girmezsiniz htmlentities!!!! Bu gerekli bir ileri geri israf kaynağıdır. kodunuzu deli tutmak !!!!


5

Bir manjaro üzerinde php 7.3.8 kullanıyorum ve Farsça içerikle çalışıyordum. Bu benim sorunumu çözdü:

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

Bu aynı tavsiye Sam tarafından aynı sayfada daha önce verilmiştir. Lütfen yedek bilgi göndermeyin.
mickmackusa

4

Benim için çalışır:

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

2
Dikkatli olun, utf8_decode bilgi kaybedebilir (a ile değiştirilir ?)
jwal

2

Doğru sonuç için kullanın

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

Bu operasyon

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

Kötü bir yoldur, çünkü & lt; , & gt; $ profilinde olabilir ve mb_convert_encoding öğesinden sonra iki kez dönüştürülmez. XSS ve yanlış HTML için delik.


1

Benim için işe yarayan tek şey kabul edilen cevaptı

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

ANCAK

Bu <?xml encoding="utf-8" ?>da belgenin çıktısında yeni sorunlar yarattı.

O zaman benim için çözüm

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

Bazı çözümler bana xmlüstbilgiyi kaldırmam gerektiğini söyledi.

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

Bu, kısmi bir belgede olduğu gibi benim için işe yaramadı (örneğin, iki <p>etiketi olan bir belge ), yalnızca <p>iade edilen etiketlerden biri .


0

Sorun, DOMDocument :: saveHTML () işlevine parametre eklediğinizde kodlamayı kaybetmenizdir. Bazı durumlarda, parametrenin kullanımından kaçınmanız ve aradığınızı bulmak için eski dize işlevini kullanmanız gerekir.

Önceki yanıtın sizin için işe yaradığını düşünüyorum, ancak bu geçici çözüm benim için işe yaramadığından, bu yanıtı benim durumumda kim olabilir yardım etmek için ekliyorum.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.