Определение кодировки.

Статус
В этой теме нельзя размещать новые ответы.
Спасибо всем за участие, появились кое какие идеи реализации, как сделаю рабочий вариант выложу его.

По поводу:

Бомба! В шоке от такого количества функционала, обязательно поковыряю на досуге.
 
Типичный пример, как из-за какой-то ерунды можно погрязнуть в коде на месяц.

Чувак, вставь в свой парсер один if.

Если тянем с такого-то сайта - то плевать, что у него в Meta utf-8 прописано, считаем что там 1251 и обрабатываем соответствующим образом. Все.

Работы на 5 минут. Но нет, т.к. мы Настоящие Программисты, а наш парсер - это ниибаться Программный Продукт, то мы не ищем легких путей, и делаем Все Правильно. А Правильно - это вот как:

Во-первых, в исходниках вордпресса в файле formatting.php есть замечательная функция seems_utf8().
PHP:
function seems_utf8($str) {
   $length = strlen($str);
   for ($i=0; $i < $length; $i++) {
      $c = ord($str[$i]);
      if ($c < 0x80) $n = 0; # 0bbbbbbb
      elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
      elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
      elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
      elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb
      elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b
      else return false; # Does not match any model
      for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
         if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
            return false;
      }
   }
   return true;
}
Она вполне рабочая, но решает только часть задачи.

Вторая часть задачи - это словарик. Или слов, или буквосочетаний по типу как в Punto Switcher, перекодированый во все возможные варианты, включая всякие "koi8-autoconverted to cp1251 as it was MAC" и т.п. Словарик делается простейшим скриптом из любого достаточно длинного русского текста известной кодировки. Вариантов его переконвертации - сколько сможешь придумать, столько и делай. Конечно, для парсера достаточно только koi8 и cp1251, но у нас же Программный Продукт, так что надо предусмотреть и остальные.

Ну и третья часть задачи - перекодировка из html-entities. Тоже есть в том же вордпрессе.

PS:
Да, разумеется это не все. Еще надо проверять, "правильно ли перекодировалось". Для этого можно завести другие словарики, со стоп-словами. Можно заюзать какой-то сторонний спелл-чекер. Можно написать свой. Короче вариантов миллион, если за эту задачу взяться как следует - то и за год можно не управиться. А можно вставить if.
 
venetu - спасибо вы как всегда вправляете мозг на нужное место.

Но осенило меня в этом плане, еще вчера вечером и понял я что задачка эта не на один день, и не стоит сильно заморачиваться, а просто необходимо использовать спасительный IF :)

В общем спасибо, только вот бы написали свой пост по раньше :)
 
Так как и обещал выкладываю чего, навоял. Я понимаю что ниже изложенное решение не являться лекарством от всех болячек, и даже не является одним из лидеров решения проблемы, но пока так, со временем все будет.

Код не оптимизирован и специально составлен в развернутом виде, привык работать с комфортом не во вред глазам и мозгу, так что если товарищи оптимизаторы захотят сократить код, то милости просим, и народ не забываем выкладывать примеры оптимизации и улучшения.

Перейдем к исходникам, функции:
PHP:
	function getPageCharset($v = 0, $v2 = false) {
		//preg_match('#<meta[^<>]*content=[\'"].*charset=(.+)[\'"][^<>]*>#iU', $v, $rm1);
		preg_match('#<meta[^<>]*content=[\'"].*(utf-8|windows-1251)[\'"][^<>]*>#iU', $v, $rm1);
		unset($rm1[0]);
		if ( $rm1[1] != '' )
		{
	    	$r = $rm1[1];
		}
		else {
	    	$r = $v2;
		}
        $r = strtolower($r);
		return ( $r ) ;
	}
Функция getPageCharset() предназначена для получения кодировки из мета тега страницы.
Принимает два значения на входе:
$v = Тело страници.
$v2 = кодировка по умолчанию
PHP:
	function chkIsUtf8($v = '', $v2 = 2) {
		$r = 0;
		#check #1
		$cV = strlen($v);
		for ($i=0; $i < $cV; $i++) {
			$c = ord($v[$i]);
			if ($c < 0x80) {
				$n = 0; # 0bbbbbbb
			}
			else if (($c & 0xE0) == 0xC0) {
				$n=1; # 110bbbbb
			}
			else if (($c & 0xF0) == 0xE0) {
				$n=2; # 1110bbbb
			}
			else if (($c & 0xF8) == 0xF0) {
				$n=3; # 11110bbb
			}
			else if (($c & 0xFC) == 0xF8) {
				$n=4; # 111110bb
			}
			else if (($c & 0xFE) == 0xFC) {
				$n=5; # 1111110b
			}
			else {
				$r = 1;
				break; # Does not match any model
			}
			for ($i2=0; $i2<$n; $i2++) { # n bytes matching 10bbbbbb follow ?
				if ((++$i == $cV) OR ((ord($v[$i]) & 0xC0) != 0x80)) {
					$r = 1;
					break(2);
				}
			}
		}
		if ($r) {
        	$r = 0;
		}
		else {
        	$r++;
		}
		#check #2
       	if (function_exists(mb_check_encoding)) {
           	if (mb_check_encoding($v, 'utf-8')) {
	        	$r++;
           	}
       	}
		#check #3
		if (preg_match('#.#u', $v)) {
        	$r++;
		}
		if ($r >= $v2) {
			$r = 1;
		}
		else {
			$r = 0;
		}
		return $r;
	}
Функция chkIsUtf8() проверяет строку на кодировку геа-8.
Принимает два значения на входе:
$v = Проверяемая строка, в нашем случае тело страницы.
$v2 = строгость проверки. Функция проверяет строку на utf-8 последовательно 3-мя способами, по умолчанию параметр строгости проверки выставлен в значение 2, то есть если в 2-х проверка результат будет положительный? то функция скажет что строка имеет кодировку utf-8
PHP:
	function getCharsetString($v = '') {
		$r = false;
		if (chkIsUtf8($v)) {
			$r = 'utf-8';
		}
		else {
			$cp1251 = 0;
			$koi8u = 0;
			$cV = strlen($v);
			for($i=0; $i<$cV; $i++) {
				$c = ord($v[$i]);
				if (($c>223 AND $c<256) OR ($c==179) OR ($c==180) OR ($c==186) OR ($c==191)) {
					$cp1251++; // а-я, і, ґ, є, Ї
				}
				if (($c>191 AND $c<224) OR ($c==164) OR ($c==166) OR ($c==167) OR ($c==173)) {
					$koi8u++; // а-я, є, і, ї, ґ
				}
			}
			if ($cp1251>$koi8u) {
				$r = 'windows-1251';
			}
			else {
				$r = 'koi8-u';
			}
		}
		return $r;
	}
Функция getCharsetString() возвращает кодировку строки, сейчас поддерживает определение следующих кодировок utf-8, windows-1251, koi8-u, и зависит от функции chkIsUtf8().
Принимает одно значения на входе:
$v = Проверяемая строка, в нашем случае тело страницы.
PHP:
	function chkCharsetPage($v = '', $v2 = false) {
        $charset1 = $v2;
		if ($v != '') {
        	$charset2 = TCorE_helper::getPageCharset($v);
			$charset3 = TCorE_helper::getCharsetString($v);
		}
		else {
        	$charset2 = false;
			$charset3 = false;
		}
		if (($charset1 != false) AND ($charset1 == $charset2 OR $charset1 == $charset3)) {
           	$r = $charset1;
		}
		else if ($charset2 != false AND $charset2 == $charset3) {
           	$r = $charset2;
		}
		else if ($charset1 != false AND $charset2 == false) {
			$r = $charset1;
		}
		else if ($charset3 != false) {
			$r = $charset3;
		}
		else {
			$r = false;
		}
		return $r;
	}
Функция chkCharsetPage() проверяет и возвращает кодировку страницы.
Принимает два значения на входе:
$v = Проверяемая строка, в нашем случае тело страницы.
$v2 = выпарсеная кодировка из заголовков ответа сервера.
запуск работы:
PHP:
	$pageBody = '...ПОЛУЧАЕМ ТЕЛО СТРАНИЦЫ...';
	$pageHeaderCharset = '...РАСПАРСИВАЕМ ЗАГОЛОВКИ ПЕРЕДАННЫЕ НАМ СЕРВЕРОМ НА НАЛИЧИЕ КОДИРОВКИ...';
	$pageBody = preg_replace( '#(\n|\r|\t| {2,})#', '', $pageBody);
	echo chkCharsetPage($pageBody, $pageHeaderCharset);


Теперь хочу немного рассказать ка да чего, собственно функция chkCharsetPage() являеться локомотивом для всех остальных функций, и заниматься конечным определением кодировки страницы.
Работа функции основывается на следующих принципах и правилах:
1) Определение по возможности, вариантов кодировки в 3 местах:

1.1) Заголовки ответа сервера

1.2) Мета данные страницы

1.3) Самостоятельный анализ тела документа на пренадлежность к кодировкам.


2) Выполнение правил определения кодировки (всего 5:(

2.1) Если БЫЛА ПОЛУЧЕНА кодировка из Заголовков ответа сервера И она РАВНА кодировке Мета данных страницы ИЛИ кодировке Самостоятельного анализа, ТО возвращается кодировка Заголовков ответа сервера. В ИНОМ случае выполняется следующее правило.

2.2) Если БЫЛА ПОЛУЧЕНА кодировка Мета данных страницы И она РАВНА кодировке Самостоятельного анализа, ТО возвращается кодировка Мета данных страницы. В ИНОМ случае выполняется следующее правило.

2.3) Если БЫЛА ПОЛУЧЕНА кодировка из Заголовков ответа сервера И кодировка из Мета данных страницы НЕ ПОЛУЧЕНА, ТО возвращается кодировка Заголовков ответа сервера. В ИНОМ случае выполняется следующее правило.

2.4) Если БЫЛА ПОЛУЧЕНА кодировка Самостоятельного анализа, ТО возвращается кодировка Самостоятельного анализа. В ИНОМ случае выполняется следующее правило.

2.5) В ОСТАЛЬНЫХ случаях возвращается false.


Ну в общем как то так:)
Советы, замечания, предложения в студию.

P.S.: Извиняюсь за такой огромный пост.
 
Статус
В этой теме нельзя размещать новые ответы.
Назад
Сверху