PHP: преобразование вывода curl exec в UTF8

Я хотел бы работать только с 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 ответов


следуя совету Гамбо и Пекки, я написал 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', 'utf-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', 'utf-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;
}

regexes в основном от http://nadeausoftware.com/articles/2007/06/php_tip_how_get_web_page_content_type


преобразование легко. Обнаружение-самая трудная часть. Вы могли бы попробовать mb_detect_encoding но это очень шаткий метод, он буквально "угадывает" тип контента, и, как подчеркивает @troelskn в комментариях, может угадать "грубые" различия в лучшем случае (это многобайтовая кодировка?) но не удается обнаружить нюансы аналогичных наборов символов.

правильным способом было бы IMO:

  • интерпретации любого content-type метатеги в страница
  • интерпретации любого content-type заголовки, отправляемые сервером
  • если это ничего не дает, попробуйте "понюхать" кодирование с помощью mb_detect_encoding()
  • если это ничего не дает, вернитесь к определенному умолчанию (возможно, ISO-8859-1, возможно, UTF-8).

отличается от изложенных в руководстве в ответе @Gumbo, я лично думаю, что мета-теги должны иметь приоритет над заголовками сервера, потому что я уверен, что если присутствует мета-тег, то является более надежным индикатором фактической кодировки страницы, чем настройка сервера, некоторые операторы сайта даже не знают, как изменить. Правильный способ, однако, по-видимому, заключается в обработке заголовков типа контента с более высоким приоритетом.

для первого, я думаю, вы можете использовать get_meta_tags (). Последнее вы должны получить от curl уже, вам просто нужно будет разобрать его. здесь является полным примером того, как систематически обрабатывать заголовки ответов, обслуживаемые локон.

преобразование затем будет использовать iconv:

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

Я был очень рад найти этот ответ, но заметил, что есть недостаток в <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. Я не мог выяснить, как разместить это в качестве комментария, как это, очевидно, не полный ответ.)


вы можете попробовать и использовать что-то вроде:

http://www.php.net/manual/en/function.mb-detect-encoding.php

http://www.php.net/manual/en/function.mb-convert-encoding.php

хотя это не дурак доказательство.


существует определенный порядок как указать кодировку символов в HTML:

[...] конформные агенты пользователей должны соблюдать следующие приоритеты при определении кодировки символов документа (от высшего приоритета к низшему):

  1. параметр HTTP "charset" в поле "Content-Type".
  2. A META объявление с" http-equiv "установлено в" Content-Type "и значение для"charset".
  3. на charset набор атрибутов для элемента, который обозначает внешний ресурс.

если нет объявления кодировки символов,HTTP определяет ISO 8859-1 как кодировку символов по умолчанию. Вы можете либо использовать это как кодировку символов по умолчанию для HTML, либо просто отказаться от обработки ответа.

для XHTML у вас дополнительно есть объявление XML как источник для кодирования:

в XML-документе кодировка документа указывается в XML-декларации (например,<?xml version="1.0" encoding="EUC-JP"?>). Для переносимого представления документов с определенными кодировками символов лучше всего убедиться, что веб-сервер предоставляет правильные заголовки. Если это невозможно, документ, который хочет явно задать свою кодировку символов, должен включать как объявление XML, объявление кодировки, так и meta инструкция http-equiv (например,<meta http-equiv="Content-type" content="text/html; charset=EUC-JP" />). В XHTML-соответствующие агенты пользователей, значение объявления кодировки объявления XML имеет приоритет.

если нет кодировки символов, XML определяет UTF-8 и UTF-16 как кодировку символов по умолчанию:

если кодировка не определяется протоколом более высокого уровня, это также является фатальной ошибкой, если объект XML не содержит объявления кодировки и его содержимое не является законным UTF-8 или UTF-16.

так, чтобы подводя итог, порядок таков:

  1. параметр HTTP "charset" в поле "Content-Type".
  2. XML-декларация с