Определение кодировки текста
Автоматическое определение кодировки текста
PHP скрипт для автоматического определения кодировки текста.
<?php
define('LOWERCASE',3);
define('UPPERCASE',1);
function detect_cyr_charset($str) {
$charsets = Array(
'k' => 0,
'w' => 0,
'd' => 0,
'i' => 0,
'm' => 0
);
for ( $i = 0, $length = strlen($str); $i < $length; $i++ ) {
$char = ord($str[$i]);
//non-russian characters
if ($char < 128 || $char > 256) continue;
//CP866
if (($char > 159 && $char < 176) || ($char > 223 && $char < 242))
$charsets['d']+=LOWERCASE;
if (($char > 127 && $char < 160)) $charsets['d']+=UPPERCASE;
//KOI8-R
if (($char > 191 && $char < 223)) $charsets['k']+=LOWERCASE;
if (($char > 222 && $char < 256)) $charsets['k']+=UPPERCASE;
//WIN-1251
if ($char > 223 && $char < 256) $charsets['w']+=LOWERCASE;
if ($char > 191 && $char < 224) $charsets['w']+=UPPERCASE;
//MAC
if ($char > 221 && $char < 255) $charsets['m']+=LOWERCASE;
if ($char > 127 && $char < 160) $charsets['m']+=UPPERCASE;
//ISO-8859-5
if ($char > 207 && $char < 240) $charsets['i']+=LOWERCASE;
if ($char > 175 && $char < 208) $charsets['i']+=UPPERCASE;
}
arsort($charsets);
return key($charsets);
}
?>
Определение кодировки страницы сайта.
Чтение страницы сайта и преобразование в UTF-8 или в Windows-1251
При формированнии карт сайтов с помощью сервиса периодически сталкивался с проблемами некорректного указания кодировки страницы или неуказания кодовой страницы вообще. В настоящий момент у меня работает функция анализа кодовой страницы похожая на представленную ниже.
Представленный ниже пример читает страницу, преобразует её в UTF-8, загружает в DOM-объект, получает из него title и выводит его в кодировке windows-1251.
<form method=\"get\">
Страница для анализа: <input type=\"text\" name=\"url\">
</form>
PHP анализ и обработка:
<?php
$url=@$_GET['url'];
echo "<br>Страница <b>".$url."</b><br>\n";
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER,true);
curl_setopt($curl, CURLOPT_HEADER, 1); // включать header в вывод
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // следовать любому "Location: " header
curl_setopt($curl, CURLOPT_TIMEOUT, 20); // максимальное время в секундах, для работы CURL-функций.
$html = @curl_exec($curl);
@file_put_contents($file,$html);
$header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
curl_close($curl); // close cURL handler
$headers = substr($html, 0, $header_size - 4)."\n";
$body = substr($html, $header_size);
if (preg_match("|Content-Type: .*?charset=(.*)\n|imsU", $headers, $results))$ct0=trim($results[1]);
else $ct0=false;
if (preg_match_all('/(<meta\s*http-equiv=[\'\"]Content-Type[\'\"]\s*content=[\'\"][^;]*;\s*charset=([^\"\']*?)(?:"|\;|\')[^>]*>)/i',$body,$arr,PREG_PATTERN_ORDER)){
$ct1=strtolower(trim($arr[2][0]));
// не учитываю, что заголовки могут быть разными. Это в платной версии.
if ($ct1=='utf-8')$ct1=false;
$ct0=false; // meta не добавлять
}else $ct1=false;
if ($ct1){
echo "<br>Преобразование ".$ct1." -> UTF-8";
$body=@iconv($ct1,'utf-8//IGNORE',$body);
}
// добавляю в head Content-Type
if($ct0)$body=preg_replace('/<head[^>]*>/','<head>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
',$body);
$doc = new DOMDocument();
@$doc->loadHTML($body);
if ( ($tags = @$doc->getElementsByTagName('title')) ) {
$title = @$tags->item(0)->nodeValue;
print "<br>Заголовок страницы: ".@iconv("UTF-8", "windows-1251//IGNORE", $title).'<br />';
}
?>
Приведенный пример упрощен и не содержит обработку "трудных" случаев, когда кодовая страница в header указанна одна, а в META другая. Также не содержит обработку двойных мета с разными кодовыми страницами(и такое у меня попадалось). Полный пример дополнительно осуществляет кеширование и "борется" с кривыми страницами. Представленный код будет корректно работать на 95% страниц/сайтов в рунете.
Определение кодировки curl страницы
После получения страницы с помощью Curl-запроса Возникает необходимость привести её в требуемую кодирвку. Но вот тут и кроется "подвох". Мы часто не знаем начальную кодировку, из которой нужно перекодировать. Роясь в интернете, я нашел интересное решение charset_x_win, скачать можно здесь. Решение есть, но очень тяжелое. Потом я попробовал проанализировать что есть среди стандартных функций PHP... И нашел решение:
Определение кодировки полученной строки:
<?php
print mb_detect_encoding($str,"auto")."<br>\n"; // ASCII или UTF-8
?>
Преобразование кодировки полученной строки:
<?php
print mb_convert_encoding($str, "windows-1251", "auto")."<br>\n";
?>
Отправка писем в правильной кодировке на PHP
У меня часто спрашивают, как отсылать письма с помощью PHP в правильной кодировке: после отправки письма с русскими буквами, на почту людям приходит абракадабра.
А все дело в том, что для почты у нас принято использовать кодировку KOI8-R, а для веба — Win-1251 (ANSI) — так, и выходит — мы шлем с сайтов в win-1251, а почтовые программы открывают его как KOI8-R...
Выходов из ситуации хватает. Начиная, можно на самой веб странице можно использовать кодировку KOI8-R. Но я бы не советовал этого не делать — у некоторых посетителей могут возникнуть с кодировкой на самой странице. Так что оставим этот метод для ламеров, а применим более изящный, на PHP.
Итак, что же можно сделать? Первое что приходит в голову — а не проще ли перекодировать само сообщение? И в правду, нет нечего проще.
Тем более в PHP предусмотрена стандартная процедура — convert_cyr_string(string str, string from, string to); как Вы уже, наверное, догадались, функция перекодирует текст, содержащийся в переменной $str из кодировки $from в кодировку $to. Указание кодировки происходит посредством символов латинского алфавита, вот доступные параметры и соответствующие им кодировки:
k — KOI8-R
w — Win-1252
i — ISO8859-5
a — X-CP866
d — X-CP866
m — X-Mac-Cyrillic
Как видно из таблицы, в нашем случае, перед отправкой сообщения, содержащегося в переменной $msg, его нужно перекодировать из w в k (из Win-1251 в KOI8-R), это выглядит так: $msg = convert_cyr_string ($msg,w,k); вот и все, теперь можно спокойно отсылать наше письмо.
Проблема с кодировкой UTF-8 для XML
Столкнулся с одним неприятным багом PHP при работе с XML с кодировкой UTF-8. Если воспользоваться функцией DOMDocument::loadXML.
Есть исходный xml-файл: 1.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<name>Имя</name>
<description>Description</description>
</root>
Теперь хотим воспользоваться DOMDocument и подгрузить файл, далее дампим его содержимое на экран. В этом примере все будет работать на ура и результат будет идентичен файлу 1.xml.
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->load('1.xml');
echo $dom->saveXML();
Рассмотрим другую ситуацию, когда мы xml-данные как-то получаем, меняем, добавляем и в итоге передаем строку в функцию loadXML.
$xml = <<<XML
<root>
<name>Имя</name>
<description>Description</description>
</root>
XML;
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->loadXML($xml);
echo $dom->saveXML();
А на выходе получаем нижеследующее. Куда делись наши символы на русском? И исчезла кодировка UTF-8?
<?xml version="1.0"?>
<root>
<name>Имя</name>
<description>Description</description>
</root>
Решение: нужно добавить заголовок xml-файла с кодировкой. Надеюсь в версии PHP 6 они сделают поддержку UTF-8 получше.
<?php
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->loadXML('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $xml);
echo $dom->saveXML();
?>
Не работает функция iconv(), а нужно конвертнуть из UTF-8 в windows-1251
Что же делать, когда на хостинге не работает функция iconv() и php выдает Call to undefined function: iconv(), а нужно конвертнуть из UTF-8 в windows-1251
На помощь прийдет следующая функция:
function utf8_to_win($string){
for ($c=0;$c<strlen($string);$c++){
$i=ord($string[$c]);
if ($i <= 127) @$out .= $string[$c];
if (@$byte2){
$new_c2=($c1&3)*64+($i&63);
$new_c1=($c1>>2)&5;
$new_i=$new_c1*256+$new_c2;
if ($new_i==1025){
$out_i=168;
} else {
if ($new_i==1105){
$out_i=184;
} else {
$out_i=$new_i-848;
}
}
@$out .= chr($out_i);
$byte2 = false;
}
if (($i>>5)==6) {
$c1 = $i;
$byte2 = true;
}
}
return $out;
}
?>
Не работает функция iconv(), а нужно конвертнуть из UTF-8 в windows-1251
Что же делать, когда на хостинге не работает функция iconv() и php выдает Call to undefined function: iconv(), а нужно конвертнуть из UTF-8 в windows-1251
На помощь прийдет следующая функция:
function utf8_to_win($string){
for ($c=0;$c<strlen($string);$c++){
$i=ord($string[$c]);
if ($i <= 127) @$out .= $string[$c];
if (@$byte2){
$new_c2=($c1&3)*64+($i&63);
$new_c1=($c1>>2)&5;
$new_i=$new_c1*256+$new_c2;
if ($new_i==1025){
$out_i=168;
} else {
if ($new_i==1105){
$out_i=184;
} else {
$out_i=$new_i-848;
}
}
@$out .= chr($out_i);
$byte2 = false;
}
if (($i>>5)==6) {
$c1 = $i;
$byte2 = true;
}
}
return $out;
}
?>
Раскодируем текст из base64
<?
$string="TXlOYW1l"; # раскодируемое
$decoded=base64_decode($string);
echo $decoded
// MyName
?>
Кодируем текст в base64
<?
$string="MyName"; # кодируемое
$encoded=base64_encode($string);
echo $encoded
// TXlOYW1l
?>
ASCII to HEX средствами PHP
Для этого воспользуемся 4-я функциями:
strlen - подсчитаем кол-во символов
substr - возвращает часть строки
ord - возвращает ASCII-значение символа
dechex - 10-ричное в 16-ричное
и напишем несложный замысловатый код:
<?php
$my_url='microsoft.com';
$strlength = strlen($my_url);
for($i=0; $i<$strlength; $i++){
$returnval .= '%'.dechex(ord(substr($my_url, $i, 1)));
}
echo 'http://'.$returnval;
?>
Выполнив этот скрипт мы получим адрес мелкомягких в таком виде http://%6d%69%63%72%6f%73%6f%66%74%2e%63%6f%6d