Псевдо-XML
Сейчас пробую сделать что-то типа псевдо-xml. Никаких модулей у хостера не стоит, поэтому приходится разбирать все «вручную» с помощью PHP.Нашел в Сети вот это. Простая функция, которая «...extracts the content of all tags ($tag) in string ($string). When ($mode) is 1 it returns the content as an array, otherwise as a string». То есть вы пишите $quotetext=untag ($string, "blockquote", 0) и получаете текст, который заключен в <blockquote>этот тэг</blockquote>.
function untag ($string, $tag, $mode)
{
$tmpval="";
$preg="/<".$tag.">(.*?)</".$tag.">/si";
preg_match_all ($preg,$string,$tags);
foreach ($tags[1] as $tmpcont) {
if ($mode==1){$tmpval[]=$tmpcont;}
else {$tmpval.=$tmpcont;}
}
return $tmpval;
}
Регулярные выражения рулят, но... KISS-принцип гласит: «Keep It Simple, Stupid». Очень часто можно обойтись без регулярных выражений, используя функции, которые работают быстрее. Это известная фишка — что strpos работает очень быстро и в некоторых случаях может запросто заменить некоторые регулярные выражения.
А теперь — сюрприз! Если пожертвовать «универсальностью» (например, выдачей текста в виде array’а или нечувствительностью к регистру тэгов), то можно сделать так, чтобы эта функция работала в восемь раз быстрее.
Таким вот образом —
function tags ($string, $tag)
{
$z=strpos ($string, "<".$tag.">")+strlen ($tag)+2;
$s=substr ($string, $z, strpos ($string, "</".$tag.">")-$z);
return $s;
}
Итого: 1) Можно использовать обе функции, первую — когда надо получить именно array, вторую — когда надо получить только 1-ое вхождение и вы точно знаете, в каком регистре пишутся тэги. 2) Часто можно обойтись без регулярных выражений.
Применение: для движков сайтов. Например, дату можно писать как <date>32 мартобря</date> в любом месте текста. А потом выкусывать с помощью этой функции. Получаем что и хотели — «псевдо-ХМL».
Для полноты картины добавим обработку ошибок (если ничего не найдено):
function untag ($string, $tag)
{
$z = strpos ($string, '<'.$tag.'>');
if ($z!==false) {
$z=$z+ strlen ($tag) + 2;
$z2 = strpos ($string, '</'.$tag.'>');
$s = substr ($string, $z, $z2 - $z);
return $s;
}
};
Для тех, кто в танке! В комментариях к данной заметке нашелся умник, который хочет «всего-лишь показать, что главный недостаток регулярных выражений — нежелание в них разбираться». При этом предлагает свой пример, который работает как минимум в шесть раз медленней. Что критично. Уважаемый! Главный недостаток регулярных выражений — это то, что некоторые люди, которые «разобрались» в регулярных выражениях, пихают их куда ни попадя.
Update: От читателя пришел апдейт этой функции. Теперь она может вырезать тэги с параметрами, типа <td ...>...</td> и складывать содержимое одинаковых тэгов в array.
// (C) Шушпанов Пётр Анатольевич function xmf($string, $tag) { $string = ' function tags($string) { function xmf($string, $tag) { print_r($xmf);<?
// mail: web@phystech.ru
// Очередной вариант: теперь может вырезать контент из тэгов с параметрами, типа <p class=...>...</p>, <td ...>...</td>.
while(true):
//начало тэга
$start = strpos($string, "<".$tag, $stop);
if ($start === false)
break;
//начало контента
$start = strpos($string, ">", $start);
if ($start === false)
break;
$start++;
//конец контента
$stop = strpos($string, "</".$tag.">", $start);
if ($stop === false)
break;
//выкусить контент!
$result[] = substr($string, $start, $stop - $start);
endwhile;
return $result;
}
// Далее можно ещё тем же методом выковырять все тэги автоматически, а
// потом весь контент распихать в массив поименованый тэгами. У меня на машине
// такой скриптех выполняется .00011 с
// А функцию я назвал XMF - "XML Fake" (ИксЭмЭль импровизированный) :)
<news>один</news>
<news>два</news>
<news>три</news>
<news>четыре</news>
<menu>a</menu>
<menu>b</menu>
<menu>c</menu>
<menu>d</menu>
<title>aaaa</title>';
$tags = tags($string);
foreach ($tags as $val)
$xmf[$val] = xmf($string, $val);
while(true):
$start = strpos($string, '<', $stop);
if ($start === false)
break;
$start = $start + strlen($tag) + 1;
$stop = strpos($string, '>', $start);
if ($stop === false)
break;
$res = substr($string, $start, $stop - $start);
if (strpos($res, '/') === false && !in_array($res,(array)$result)):
$result[] = $res;
endif;
endwhile;
return $result;
}
while(true):
$start = strpos($string, "<".$tag.">", $stop);
if ($start === false)
break;
$start = $start + strlen($tag) + 2;
$stop = strpos($string, "</".$tag.">", $start);
if ($stop === false)
break;
$result[] = substr($string, $start, $stop - $start);
endwhile;
return $result;
}
?>