2012-07-03 57 views
50

Máy chủ web đang phân phối phản hồi bằng mã hóa utf-8, tất cả các tệp được lưu với mã hóa utf-8 và mọi thứ tôi biết đã được đặt thành mã hóa utf-8 .PHP DomDocument không xử lý các ký tự utf-8 (☆)

Dưới đây là một chương trình nhanh chóng, để kiểm tra nếu kết quả hoạt động:

<?php 
$html = <<<HTML 
<!doctype html> 
<html> 
<head> 
    <meta charset="utf-8"> 
    <title>Test!</title> 
</head> 
<body> 
    <h1>☆ Hello ☆ World ☆</h1> 
</body> 
</html> 
HTML; 

$dom = new DomDocument("1.0", "utf-8"); 
$dom->loadHTML($html); 

header("Content-Type: text/html; charset=utf-8"); 
echo($dom->saveHTML()); 

Đầu ra của chương trình là:

<!DOCTYPE html> 
<html><head><meta charset="utf-8"><title>Test!</title></head><body> 
    <h1>&acirc;&#152;&#134; Hello &acirc;&#152;&#134; World &acirc;&#152;&#134;</h1> 
</body></html> 

Những ám như:

â~ † Xin chào, â ˜ † Thế giới â ˜


Tôi có thể làm gì sai? Tôi phải cụ thể hơn bao nhiêu để nói cho DomDocument xử lý đúng utf-8?

+0

Cảm ơn bạn đã đưa ra câu hỏi, một câu hỏi tương tự là: [Làm thế nào để giữ ngôn ngữ Trung Quốc hoặc ngoại ngữ thay vì chuyển đổi thành mã?] (Http: // stackoverflow .com/q/10237238/367456) tuy nhiên bạn có thể xem xét việc hack. – hakre

+0

Related: [Yêu cầu PHP # 47875 - Không có tùy chọn để thiết lập mã hóa đầu vào HTML] (https://bugs.php.net/bug.php?id=47875) – hakre

+1

Kỳ lạ đủ: tài liệu php nói: 'Phần mở rộng DOM sử dụng mã hóa UTF-8. Sử dụng utf8_encode() và utf8_decode() để làm việc với văn bản trong mã hóa ISO-8859-1 hoặc Iconv cho các mã hóa khác.' xem: http://www.php.net/manual/en/intro.dom.php – jens

Trả lời

107

DOMDocument::loadHTML() sẽ là một chuỗi HTML.

HTML sử dụng mã hóa ISO-8859-1 (ISO Latin Alphabet No. 1) làm mặc định cho mỗi thông số kỹ thuật của nó. Đó là từ lâu hơn, xem 6.1. The HTML Document Character Set. Trong thực tế đó là nhiều hơn sự hỗ trợ mặc định cho Windows-1252 trong webbrowsers phổ biến.

Tôi quay lại điều đó vì DOMDocument của PHP dựa trên libxml và mang lại HTMLparser được thiết kế cho HTML 4.0.

Tôi muốn đảm bảo rằng bạn có thể giả định rằng bạn có thể tải chuỗi được mã hóa ISO-8859-1.

Chuỗi của bạn là UTF-8 được mã hóa. Chuyển tất cả các ký tự cao hơn 127/h7F thành HTML Entities và bạn ổn. Nếu bạn không muốn làm điều đó của riêng mình, đó là những gì mb_convert_encoding với mã hóa HTML-ENTITIES mục tiêu thực hiện:

  • Những nhân vật đã được đặt tên đơn vị, sẽ nhận được entitiy đặt tên. € -> &euro;
  • Những người khác nhận thực thể dạng số (thập phân) của họ, ví dụ: ☆ -> &#9734;

Sau đây là một ví dụ mã mà làm cho sự tiến bộ một chút rõ ràng hơn bằng cách sử dụng một hàm callback:

$html = preg_replace_callback('/[\x{80}-\x{10FFFF}]/u', function($match) { 
    list($utf8) = $match; 
    $entity = mb_convert_encoding($utf8, 'HTML-ENTITIES', 'UTF-8'); 
    printf("%s -> %s\n", $utf8, $entity); 
    return $entity; 
}, $html); 

này gương mẫu đầu ra cho chuỗi của bạn:

☆ -> &#9734; 
☆ -> &#9734; 
☆ -> &#9734; 

Dù sao, đó chỉ là để tìm hiểu sâu hơn về chuỗi của bạn. Bạn muốn có nó hoặc chuyển đổi thành một mã hóa loadHTML có thể đối phó với. Điều đó có thể được thực hiện bằng cách chuyển đổi tất cả bên ngoài của US-ASCII thành các thực thể HTML:

$us_ascii = mb_convert_encoding($utf_8, 'HTML-ENTITIES', 'UTF-8'); 

Đảm bảo đầu vào của bạn thực sự được mã hóa UTF-8.Nếu bạn có cả mã hóa hỗn hợp (có thể xảy ra với một số đầu vào), thì mb_convert_encoding chỉ có thể xử lý một mã hóa trên mỗi chuỗi. Tôi đã vạch ra ở trên làm thế nào để cụ thể hơn làm thay thế chuỗi với sự giúp đỡ của các biểu thức chính quy, vì vậy tôi để lại thêm chi tiết cho bây giờ.

Cách thay thế khác là gợi ý mã hóa. Điều này có thể được thực hiện trong trường hợp của bạn bằng cách sửa đổi tài liệu và thêm

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

là Loại nội dung chỉ định bộ ký tự. Đó cũng là phương pháp hay nhất cho chuỗi HTML không có sẵn thông qua máy chủ web (ví dụ: được lưu trên đĩa hoặc bên trong một chuỗi như trong ví dụ của bạn). Máy chủ web thường đặt đó làm tiêu đề phản hồi.

Nếu bạn không quan tâm những lời cảnh báo thất lạc, bạn chỉ có thể thêm nó ở phía trước của chuỗi:

$dom = new DomDocument(); 
$dom->loadHTML('<meta http-equiv="content-type" content="text/html; charset=utf-8">'.$html); 

mỗi HTML 2.0 thông số kỹ thuật, yếu tố mà chỉ có thể xuất hiện trong phần <head> của một tài liệu , sẽ được tự động đặt ở đó. Đây cũng là những gì xảy ra ở đây. Sản lượng (khá-in):

<!DOCTYPE html> 
<html> 
    <head> 
    <meta http-equiv="content-type" content="text/html; charset=utf-8"> 
    <meta charset="utf-8"> 
    <title>Test!</title> 
    </head> 
    <body> 
    <h1>☆ Hello ☆ World ☆</h1>  
    </body> 
</html> 
+2

@hakre: đó là hoàn hảo! bạn giải quyết vấn đề nghiêm trọng của tôi và bây giờ tôi không có đau đầu !! – Aliweb

+1

+1 Câu trả lời hay, nhưng bạn đề xuất phương pháp nào - sử dụng 'mb_convert_encoding()' hoặc thêm thẻ meta vào 'loadHTML()'? – Nate

+1

@Nate: Tôi sẽ nói điều đó phụ thuộc. Tôi thường không khuyên bạn nên 'mb_convert_encoding()' nhưng đối với trường hợp này tôi làm bằng cách nào đó. Tuy nhiên đó là một chi tiết của sở thích cá nhân. Và nó vẫn còn phụ thuộc vào việc bạn muốn thực hiện chuyển đổi trong bước riêng của nó hay bạn chỉ muốn đập nó vào trong 'DOOMDocument :: loadHTML()' làm rò rỉ phần tử meta vào tài liệu. Tôi không biết ví dụ điều gì sẽ xảy ra nếu yếu tố đó đã tồn tại. Tôi chưa bao giờ thử nghiệm đó đến một điểm tiết kiệm, nhưng nó thường "chỉ hoạt động" (tm). Các cách khác nhau trong câu trả lời là nhiều hơn để giải thích. – hakre

12
<?php 
    header("Content-type: text/html; charset=utf-8"); 
    $html = <<<HTML 
<!doctype html> 
<html> 
<head> 
    <meta charset="utf-8"> 
    <title>Test!</title> 
</head> 
<body> 
    <h1>☆ Hello ☆ World ☆</h1> 
</body> 
</html> 
HTML; 

    $html = mb_convert_encoding($html, 'HTML-ENTITIES', "UTF-8"); 
    $dom = new DomDocument("1.0", "utf-8"); 
    $dom->loadHTML($html); 

    header("Content-Type: text/html; charset=utf-8"); 
    echo($dom->saveHTML()); 

Output:

<!DOCTYPE html> 
<html><head><meta charset="utf-8"><title>Test!</title></head><body> 
    <h1>&#9734; Hello &#9734; World &#9734;</h1> 
</body></html> 
+1

@powtac: Những biến thể này thực sự không cần dòng 'header' đó. Tất cả các nhân vật không phải là một phần của chúng tôi-ascii là các thực thể ở đây. Bất kỳ trình duyệt nào trên trái đất sẽ luôn hiển thị đúng cách này trừ khi bạn chỉ định mã hóa (sai) không chia sẻ chúng tôi-ascii. Nhưng chỉ cần lưu ý, nó cũng không sai. – hakre

15

Có một sửa chữa nhanh hơn cho rằng, sau khi tải tài liệu html của bạn trong DOMDocument, bạn chỉ cần thiết lập (hay nói đúng hơn reset) bảng mã gốc. Dưới đây là một mã mẫu:

$dom = new DOMDocument(); 
$dom->loadHTML('<?xml encoding="UTF-8">' . $html); 

foreach ($dom->childNodes as $item) 
    if ($item->nodeType == XML_PI_NODE) 
     $dom->removeChild($item); 
$dom->encoding = 'UTF-8'; // reset original encoding 
+0

Điều này làm việc tốt hơn phiên bản của hakre thêm thẻ meta vì thêm các lớp meta đã loại bỏ khỏi html tag –

+4

Hmm, câu trả lời này giống như một déjà-vu - http://stackoverflow.com/a/10834989/367456 – hakre

+0

Hmm, có thể là ..Tôi đã có mã trong một txt với một loạt các đoạn mã hữu ích. Tôi không cho rằng đó là một số nội dung gốc mặc dù đó là một số sử dụng khá chuẩn của lớp DOMDocument. – DeZeA

Các vấn đề liên quan