PHP:将curl_exec输出转换为UTF8

时间:2010-03-24 19:41:20

标签: php unicode utf-8

我想只使用UTF8。问题是我不知道每个网页的字符集。如何检测并转换为UTF8?

<?php
$url = "http://vkontakte.ru";
$ch = curl_init($url);
$options = array(
    CURLOPT_RETURNTRANSFER => true,
);
curl_setopt_array($ch, $options);
$data = curl_exec($ch);

// $data = magic($data);

print $data;

请参阅:http://paulisageek.com/tmp/curl-utf8

什么是magic()

5 个答案:

答案 0 :(得分:25)

按照Gumbo和Pekka的建议,我写了curl_exec_utf8

/** The same as curl_exec except tries its best to convert the output to utf8 **/
function curl_exec_utf8($ch) {
    $data = curl_exec($ch);
    if (!is_string($data)) return $data;

    unset($charset);
    $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);

    /* 1: HTTP Content-Type: header */
    preg_match( '@([\w/+]+)(;\s*charset=(\S+))?@i', $content_type, $matches );
    if ( isset( $matches[3] ) )
        $charset = $matches[3];

    /* 2: <meta> element in the page */
    if (!isset($charset)) {
        preg_match( '@<meta\s+http-equiv="Content-Type"\s+content="([\w/]+)(;\s*charset=([^\s"]+))?@i', $data, $matches );
        if ( isset( $matches[3] ) ) {
            $charset = $matches[3];
            /* In case we want do do further processing downstream: */
            $data = preg_replace('@(<meta\s+http-equiv="Content-Type"\s+content="[\w/]+\s*;\s*charset=)([^\s"]+)@i', '$1utf-8', $data, 1);
        }
    }

    /* 3: <xml> element in the page */
    if (!isset($charset)) {
        preg_match( '@<\?xml.+encoding="([^\s"]+)@si', $data, $matches );
        if ( isset( $matches[1] ) ) {
            $charset = $matches[1];
            /* In case we want do do further processing downstream: */
            $data = preg_replace('@(<\?xml.+encoding=")([^\s"]+)@si', '$1utf-8', $data, 1);
        }
    }

    /* 4: PHP's heuristic detection */
    if (!isset($charset)) {
        $encoding = mb_detect_encoding($data);
        if ($encoding)
            $charset = $encoding;
    }

    /* 5: Default for HTML */
    if (!isset($charset)) {
        if (strstr($content_type, "text/html") === 0)
            $charset = "ISO 8859-1";
    }

    /* Convert it if it is anything but UTF-8 */
    /* You can change "UTF-8"  to "UTF-8//IGNORE" to 
       ignore conversion errors and still output something reasonable */
    if (isset($charset) && strtoupper($charset) != "UTF-8")
        $data = iconv($charset, 'UTF-8', $data);

    return $data;
}

正则表达式主要来自http://nadeausoftware.com/articles/2007/06/php_tip_how_get_web_page_content_type

答案 1 :(得分:4)

转换很容易。检测是困难的部分。你可以尝试mb_detect_encoding,但这是一个非常不稳定的方法,它实际上是“猜测”内容类型,并且评论中的@troelskn高亮可以最好地猜测“粗略”差异(它是多字节编码吗?)但未能检测到类似字符集的细微差别。

正确的方法是IMO:

  • 解释页面中的所有content-type元标记
  • 解释服务器发送的任何content-type标头
  • 如果没有产生任何结果,请尝试使用mb_detect_encoding()
  • “嗅探”编码
  • 如果没有产生任何结果,则回退到定义的默认值(可能是ISO-8859-1,也许是UTF-8)。

与@ Gumbo的答案中的指南不同,我个人认为Meta标签应优先于服务器标头,因为我很确定如果存在Meta标签,那么这是一个更可靠的实际编码指示页面比服务器设置一些网站运营商甚至不知道如何改变。但是,正确的方法似乎是处理具有更高优先级的内容类型标头。

对于前者,我认为你可以使用get_meta_tags()。后者你应该已经从curl获得,你只需要解析它。 Here是如何系统地处理cURL提供的响应标头的完整示例。

转换将使用iconv

$new_content = iconv("incoming-charset", "utf-8", $content);

答案 2 :(得分:1)

我非常高兴能找到这个答案,但注意到<meta>标签检测存在缺陷。它似乎与任何内容类型标签都不匹配,而且还没有为新的HTML5样式标签配备:<meta charset="UTF-8">。所以我写了这篇文章,希望它可以帮助你们,并再次感谢这个出色的解决方案!

/* 2: <meta> element in the page */
if (!isset($charset)) {
    preg_match('/<[\s]*meta[^>]*charset="?([^\s"]+)\s?"/i', $data, $matches);

    if (isset($matches[1])) {
        $charset = $matches[1];
    }
}

(P.S。我无法弄清楚如何将其作为评论发布,因为它显然不是一个完整的答案。)

答案 3 :(得分:0)

答案 4 :(得分:0)

已定义订单how to specify the character encoding in HTML

  在确定文档的字符编码时(从最高优先级到最低优先级),符合条件的用户代理必须遵守以下优先级:

     
      
  1. “Content-Type”字段中的HTTP“charset”参数。
  2.   
  3. META声明,其中“http-equiv”设置为“Content-Type”,值为“charset”设置。
  4.   
  5. 在指定外部资源的元素上设置charset属性。
  6.   

如果不存在字符编码声明,HTTP defines ISO 8859-1 as default character encoding。您也可以将其用作HTML的默认字符编码,或者只是拒绝处理响应。

对于XHTML,您还有XML declaration as source for the encoding

  

在XML文档中,文档的字符编码在XML声明中指定(例如,<?xml version="1.0" encoding="EUC-JP"?>)。为了便携地呈现具有特定字符编码的文档,最好的方法是确保Web服务器提供正确的标头。如果无法做到这一点,那么想要明确设置其字符编码的文档必须同时包含XML声明和编码声明以及meta http-equiv语句(例如<meta http-equiv="Content-type" content="text/html; charset=EUC-JP" />)。在符合XHTML的用户代理中,XML声明的编码声明的值优先。

如果没有字符编码声明,XML defines UTF-8 and UTF-16 as default character encoding

  

除非编码由更高级别的协议确定,否则如果XML实体不包含编码声明且其内容不是合法的UTF-8或UTF-16,则也是致命错误。

总而言之,订单是:

  1. “Content-Type”字段中的HTTP“charset”参数。
  2. 带有encoding属性的XML声明。
  3. META声明,其中“http-equiv”设置为“Content-Type”,值为“charset”设置。
  4. 如果不存在字符编码声明,您可以将ISO 8859-1视为HTML的默认编码,并且必须假定UTF-8或UTF-16作为XHTML的默认编码。