Псевдо-XML
Источник: http://spectator.ru/
Дмитрий Смирнов
Сейчас пробую сделать что-то типа псевдо-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) Шушпанов Пётр Анатольевич
// mail: web@phystech.ru
//
Очередной вариант: теперь может вырезать контент из тэгов с параметрами,
типа <p class=...>...</p>, <td ...>...</td>.
function xmf($string, $tag) {
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" (ИксЭмЭль импровизированный) :)
$string =
'
<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);
function tags($string) {
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;
}
function xmf($string, $tag) {
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;
}
print_r($xmf);
?>