Замена функции ereg_replace()... Подскажите самый быстрый вариант...

lamerz

Новичок
Замена функции ereg_replace()... Подскажите самый быстрый вариант...

Здравствуйте!

Есть старый скрипт в котором присутствует много конструкций типа
PHP:
$data             = ereg_replace(".*<slaveData>", "", $data);
$data             = ereg_replace("</slaveData>.*", "", $data);
В голову пришел вот такой вариант на замену...
PHP:
list(,$data) = explode("<slaveData>", $data , 2);
list($data,) = explode("</slaveData>", $data , 2);
Задача заключается в повышении производительности и лишения скрипта от функций, которые умирают...
Как вы считаете... данный вариант выглядит нормально? :) Или может кто-то посоветовал бы что-то получше :)
 

Вурдалак

Продвинутый новичок
Можно посоветовать preg_replace(). Или даже preg_match().

С другой стороны — откуда эти теги?
 

HraKK

Мудак
Команда форума
PHP:
 $data = substr( $data, strpos( '<slaveData>' ), strpos( '</slaveData>' ) );
 

Вурдалак

Продвинутый новичок
HraKK
Даже если ты хотел псевдокод выложить, третий параметр будет не таким...
 

lamerz

Новичок
Автор оригинала: Вурдалак
Можно посоветовать preg_replace(). Или даже preg_match().

С другой стороны — откуда эти теги?
Теги будут браться с программы которая слушает определенный порт и отдает плэин текст.

Спасибо за советы :)

Решил провести тесты... возможно кому-то интересно будет

Для подсчета юзал функцию microtime(1);

PHP:
$time_start = microtime(true);
............
$time_end = microtime(true);
1 string это одна строчка включающая
PHP:
$data_2 = '<users>8</users><IfIsSlave>it\'s just test! ;-)</IfIsSlave><blah>dummy</blah><asd>dummy</asd>';
а html page это html страничка.... :)


--------------
PHP:
list(,$data_my) = explode("<IfIsSlave>", $data_my , 2);
list($data_my,) = explode("</IfIsSlave>", $data_my , 2);
/* 1 string
* Started at 1268164781.969 ended at 1268164781.969
* Started at 1268164793.7756 ended at 1268164793.7756
* Started at 1268164802.8831 ended at 1268164802.8832
* Started at 1268164812.8133 ended at 1268164812.8133
* Started at 1268164827.0988 ended at 1268164827.0989
* Started at 1268164859.136 ended at 1268164859.136
* html page
* Started at 1268165761.4235 ended at 1268165761.4235
* Started at 1268165774.6263 ended at 1268165774.6263
*/
PHP:
$data_my             = ereg_replace(".*<IfIsSlave>", "", $data_my); //extract data
$data_my            = ereg_replace("</IfIsSlave>.*", "", $data_my); //extract data
/* 1 string
* Started at 1268164889.9833 ended at 1268164889.9834 (0.00011610984802246)
* Started at 1268164920.3605 ended at 1268164920.3606 (0.0001220703125)
* Started at 1268164940.2691 ended at 1268164940.2692 (0.00011920928955078)
* Started at 1268164966.7055 ended at 1268164966.7056 (0.00011801719665527)
* Started at 1268164989.311 ended at 1268164989.3111 (0.00010514259338379)
* Started at 1268165015.8015 ended at 1268165015.8017 (0.00013303756713867)
* html page
* Started at 1268165790.488 ended at 1268165790.4881 (0.00011897087097168)
* Started at 1268165822.8748 ended at 1268165822.875 (0.00011277198791504)
*/
PHP:
$data_my = preg_replace('/(.*)<IfIsSlave>/i',null,$data_my);
$data_my = preg_replace('/<\/IfIsSlave>(.*)/i',null,$data_my);
/* 1 string
* Started at 1268165107.2066 ended at 1268165107.2066
* Started at 1268165128.5477 ended at 1268165128.5477
* Started at 1268165153.5112 ended at 1268165153.5113
* Started at 1268165163.2111 ended at 1268165163.2111
* Started at 1268165172.924 ended at 1268165172.924
* Started at 1268165181.9631 ended at 1268165181.9631
* html page
* Started at 1268165860.5845 ended at 1268165860.5845
* Started at 1268165883.1944 ended at 1268165883.1944
*/
PHP:
$start = strpos($data_my, '<IfIsSlave>' ) + strlen('<IfIsSlave>');
$length = strpos($data_my, '</IfIsSlave>' ) - $start;
$data_my = substr( $data_my, $start , $length  );
/* 1 string
* Started at 1268165226.6797 ended at 1268165226.6798
* Started at 1268165233.9606 ended at 1268165233.9606
* Started at 1268165242.018 ended at 1268165242.018
* Started at 1268165247.7993 ended at 1268165247.7993
* Started at 1268165253.8756 ended at 1268165253.8756
* Started at 1268165260.4907 ended at 1268165260.4907
* html page
* Started at 1268165926.9691 ended at 1268165926.9691
* Started at 1268165937.5984 ended at 1268165937.5984
*/


тестировалось на нагрузке в 50%
Linux linux-0fiy 2.6.31.12-0.1-desktop #1 SMP PREEMPT 2010-01-27 08:20:11 +0100 i686 i686 i386 GNU/Linux



забавно.. думал preg_replace() будет работать медленнее чем explode()
буду юзать preg_replace() или substr()

как видите ereg_replace() вообще "стоит и курит в сторонке".. не думал, что будет такая значительная разница... нужно поскорее переписывать скрипт

в общем все, что я хотел узнать я узнал.. тему можно закрывать...



сам скрипт для теста
PHP:
<?php

$data_0 = <<<HTML
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>Object not found!</title>
<link rev="made" href="mailto:sandel@localhost" />
<style type="text/css"><!--/*--><![CDATA[/*><!--*/
    body { color: #000000; background-color: #FFFFFF; }
    a:link { color: #0000CC; }
    p, address {margin-left: 3em;}
    span {font-size: smaller;}
/*]]>*/--></style>
</head>

<body>
<h1>Object not found!</h1>
<p>


    The requested URL was not found on this server.



    If you entered the URL manually please check your
    spelling and try again.



</p>
<p>
If you think this is a server error, please contact
the <a href="mailto:sandel@localhost">webmaster</a>.

</p>

<IfIsSlave>it's just test! ;-)</IfIsSlave>

<h2>Error 404</h2>
<address>
  <a href="/">localhost</a><br />

  <span>Tue Mar  9 20:37:26 2010<br />
  Apache/2.2.13 (Linux/SUSE)</span>
</address>
</body>
</html>
HTML;

$data_2 = '<users>8</users><IfIsSlave>it\'s just test! ;-)</IfIsSlave><blah>dummy</blah><Scuko>dummy</Scuko>';

$data_my = $data_2;

$time_start = microtime(true);

////////////////////////////////////

//list(,$data_my) = explode("<IfIsSlave>", $data_my , 2);
//list($data_my,) = explode("</IfIsSlave>", $data_my , 2);
/* 1 string
 * Started at 1268164781.969 ended at 1268164781.969
 * Started at 1268164793.7756 ended at 1268164793.7756
 * Started at 1268164802.8831 ended at 1268164802.8832
 * Started at 1268164812.8133 ended at 1268164812.8133
 * Started at 1268164827.0988 ended at 1268164827.0989
 * Started at 1268164859.136 ended at 1268164859.136
 * html page
 * Started at 1268165761.4235 ended at 1268165761.4235
 * Started at 1268165774.6263 ended at 1268165774.6263
 */
////////////////////////////////////

//$data_my             = ereg_replace(".*<IfIsSlave>", "", $data_my); //extract data
//$data_my            = ereg_replace("</IfIsSlave>.*", "", $data_my); //extract data
/* 1 string
 * Started at 1268164889.9833 ended at 1268164889.9834 (0.00011610984802246)
 * Started at 1268164920.3605 ended at 1268164920.3606 (0.0001220703125)
 * Started at 1268164940.2691 ended at 1268164940.2692 (0.00011920928955078)
 * Started at 1268164966.7055 ended at 1268164966.7056 (0.00011801719665527)
 * Started at 1268164989.311 ended at 1268164989.3111 (0.00010514259338379)
 * Started at 1268165015.8015 ended at 1268165015.8017 (0.00013303756713867)
 * html page
 * Started at 1268165790.488 ended at 1268165790.4881 (0.00011897087097168)
 * Started at 1268165822.8748 ended at 1268165822.875 (0.00011277198791504)
 */

////////////////////////////////////

//$data_my = preg_replace('/(.*)<IfIsSlave>/i',null,$data_my);
//$data_my = preg_replace('/<\/IfIsSlave>(.*)/i',null,$data_my);
/* 1 string
 * Started at 1268165107.2066 ended at 1268165107.2066
 * Started at 1268165128.5477 ended at 1268165128.5477
 * Started at 1268165153.5112 ended at 1268165153.5113
 * Started at 1268165163.2111 ended at 1268165163.2111
 * Started at 1268165172.924 ended at 1268165172.924
 * Started at 1268165181.9631 ended at 1268165181.9631
 * html page
 * Started at 1268165860.5845 ended at 1268165860.5845
 * Started at 1268165883.1944 ended at 1268165883.1944
 */

////////////////////////////////////

//$start = strpos($data_my, '<IfIsSlave>' ) + strlen('<IfIsSlave>');
//$length = strpos($data_my, '</IfIsSlave>' ) - $start;
//$data_my = substr( $data_my, $start , $length  );
/* 1 string
 * Started at 1268165226.6797 ended at 1268165226.6798
 * Started at 1268165233.9606 ended at 1268165233.9606
 * Started at 1268165242.018 ended at 1268165242.018
 * Started at 1268165247.7993 ended at 1268165247.7993
 * Started at 1268165253.8756 ended at 1268165253.8756
 * Started at 1268165260.4907 ended at 1268165260.4907
 * html page
 * Started at 1268165926.9691 ended at 1268165926.9691
 * Started at 1268165937.5984 ended at 1268165937.5984
 */

////////////////////////////////////


$time_end = microtime(true);


(float)$time_page_load = $time_end - $time_start;
//$time_page_load = round($time_page_load, 4);

echo 'RESULT: '.$data_my.'<br />';
echo 'TIME ELAPSED: '.$time_page_load.'<br />';
echo 'Started at '.$time_start.' ended at '.$time_end;
?>
 

WBS

Новичок
Re: Замена функции ereg_replace()... Подскажите самый быстрый вариант...

Автор оригинала: lamerz
Или может кто-то посоветовал бы что-то получше :)
Самый удачный вариант, имхо:
PHP:
if (preg_match("/<slaveData>(.*)<\/slaveData>/Ui", $data, $matches))
  $data = $matches[1];
 

vovanium

Новичок
lamerz
блин кто же тебя учил такие тесты делать, неужели даже не приходило в голову, что нужно выполнять тесты в цикле тысяч 100 раз, и тогда уже сравнивать...
 

HraKK

Мудак
Команда форума
Вурдалак
на автомате, да третий параметр
strlen()-strpos-strpos
 

dimagolov

Новичок
а вам не кажется странным, что старый скрипт игнорирует возможность наличия нескольких блоков <slaveData> в данных? кроме того, разные варианты замен работают по-разному (жадно или наоборот не жадно) в такой ситуации.

вроде логично сделать нежадный preg_match_all, хотя и непонятно, как склеивать фрагменты в таком случае.
 

lamerz

Новичок
Автор оригинала: dimagolov
а вам не кажется странным, что старый скрипт игнорирует возможность наличия нескольких блоков <slaveData> в данных?
У меня такие теги не повторяются... потому задачу и не ставил таким образом
Автор оригинала: vovanium
lamerz
блин кто же тебя учил такие тесты делать, неужели даже не приходило в голову, что нужно выполнять тесты в цикле тысяч 100 раз, и тогда уже сравнивать...
В рот мне ноги!!! Не додумался я до такого... но нечего! наши так просто не сдаются!!!

И так, у нас есть новый участник!
PHP:
if (preg_match("/<slaveData>(.*)<\/slaveData>/Ui", $data, $matches)) 
  $data = $matches[1];
Я почему-то думал, что
PHP:
preg_match()
не умеет такого делать... и как-то даже в документации не удосужился посмотреть... думал он может только сказать "да, там такое есть" или "нет, ты кретин недоделанный, там такого нет"....

Поехали...

PHP:
$time_start = microtime(true);
for ($i = 0; $i < 1000000; $i++) {
  ............
}
$time_end = microtime(true);
Одна строка = $data_2;
HTML файл = $data_0;
========================================
PHP:
list(,$data_my) = explode("<IfIsSlave>", $data_my , 2);
list($data_my,) = explode("</IfIsSlave>", $data_my , 2);
Данные: Одна строка
1-ая попытка заняла 2.5081579685211 секунд
2-ая попытка заняла 2.5306408405304 секунд
3-ая попытка заняла 2.4322109222412 секунд
Данные: HTML файл
1-ая попытка заняла 4.4964470863342 секунд
2-ая попытка заняла 4.0588710308075 секунд
3-ая попытка заняла 4.5066239833832 секунд
========================================
PHP:
$data_my = ereg_replace(".*<IfIsSlave>", "", $data_my);
$data_my = ereg_replace("</IfIsSlave>.*", "", $data_my);
Данные: Одна строка
1-ая попытка заняла 45.488424777985 секунд
2-ая попытка заняла 45.49302816391 секунд
3-ая попытка заняла 45.50142121315 секунд
Данные: HTML файл
1-ая попытка заняла 383.25524306297 секунд ~ 6 минут(!)
На остальные попытки меня не хватило...
========================================
PHP:
$data_my = preg_replace('/(.*)<IfIsSlave>/si',null,$data_my);
$data_my = preg_replace('/<\/IfIsSlave>(.*)/si',null,$data_my);
Данные: Одна строка
1-ая попытка заняла 10.828891992569 секунд
2-ая попытка заняла 10.829364776611 секунд
3-ая попытка заняла 10.827105045319 секунд
Данные: HTML файл
1-ая попытка заняла 22.501627922058 секунд
2-ая попытка заняла 22.5311460495 секунд
3-ая попытка заняла 22.522021055222 секунд

хочу подчеркнуть тот факт, что при вот таком регекспе
PHP:
preg_replace('/(.*)<IfIsSlave>/i',null,$data_my);
если сравнивать с данными типа "Одна строка" операция занимает всего 2.559623003006 секунд
========================================
PHP:
 preg_match("/<IfIsSlave>(.*)<\/IfIsSlave>/Ui", $data_my, $matches);
  $data_my = $matches[1];
Данные: Одна строка
1-ая попытка заняла 3.8259620666504
2-ая попытка заняла 3.8212189674377
3-ая попытка заняла 3.8299851417542
Данные: HTML файл
1-ая попытка заняла 6.6911079883575
2-ая попытка заняла 6.7298169136047
3-ая попытка заняла 6.7365138530731
========================================
PHP:
$start = strpos($data_my, '<IfIsSlave>' ) + strlen('<IfIsSlave>');
  $length = strpos($data_my, '</IfIsSlave>' ) - $start;
  $data_my = substr( $data_my, $start , $length  );
Данные: Одна строка
1-ая попытка заняла 2.1949548721313
2-ая попытка заняла 2.1884829998016
3-ая попытка заняла 2.1917660236359
Данные: HTML файл
1-ая попытка заняла 3.7988469600677
2-ая попытка заняла 3.8107299804688
3-ая попытка заняла 3.8011410236359
========================================

substr() таки поимел всех господа )
 

WBS

Новичок
Если тут идет борьба за миллисекунды, то самым быстрым должен быть вот такой код:
PHP:
$data = substr($data0, strpos($data0,"<IfIsSlave>")+strlen("<IfIsSlave>"), -strpos($data0,"</IfIsSlave>"));
У него есть ряд недостатков (как и у любого другого варианта с substr):
- зависимость от регистра,
- непредсказуемое поведение при отсутствии любого из тегов (открывающего и/или закрывающего).

Это как раз тот самый случай, когда более быстрый код не значит лучший код, имхо.


P.S.
Я надеюсь, что проводя тест Вы учли, что любой Ваш тестовый пример изменяет исходную переменную. Таким образом, если пометить такой код в цикл без изменений, он корректно отработает только при первом проходе цикла.
 

lamerz

Новичок
Автор оригинала: WBS
Если тут идет борьба за миллисекунды, то самым быстрым должен быть вот такой код:
PHP:
$data = substr($data0, strpos($data0,"<IfIsSlave>")+strlen("<IfIsSlave>"), -strpos($data0,"</IfIsSlave>"));
работать не будет из-за третьего аргумента... вы приписали минус функции strpos()... как бэ из мухи слона не сделаешь.. :) странно конечно, что не сработало с функцией strrpos(); у меня... но к сожалению пока больше времени тестить и разбираться пока нет..

У него есть ряд недостатков (как и у любого другого варианта с substr):
- зависимость от регистра,
- непредсказуемое поведение при отсутствии любого из тегов (открывающего и/или закрывающего).
меня регистр не интересует и отсутствие тегов не допускается программой


P.S.
Я надеюсь, что проводя тест Вы учли, что любой Ваш тестовый пример изменяет исходную переменную. Таким образом, если пометить такой код в цикл без изменений, он корректно отработает только при первом проходе цикла.
Конечно учел и переписал код таким образом, что исходная переменная не меняется...


еще раз спасибо всем=)
 

WBS

Новичок
Автор оригинала: lamerz
работать не будет из-за третьего аргумента... вы приписали минус функции strpos()... как бэ из мухи слона не сделаешь.. :)
Да, действительно, я запостил какую-то ерунду :).

Видимо, самый быстрый вариант вот этот:
PHP:
$data = substr($data0, $start = strpos($data0,"<IfIsSlave>")+strlen("<IfIsSlave>"), strpos($data0,"</IfIsSlave>")-$start);
Автор оригинала: lamerz
странно конечно, что не сработало с функцией strrpos(); у меня... но к сожалению пока больше времени тестить и разбираться пока нет..
strrpos() принимает одиночный символ в качестве второго аргумента. Едва ли эта функция сможет чем-то помочь в Вашей задаче ;).
 

WBS

Новичок
Автор оригинала: HraKK
WBS
открой для себя великую функцию [m]stripos[/m]!
Другая функция - другое время выполнения программы. По моим замерам с stripos() медленнее чем с preg_match() раз в 10.
 

vovanium

Новичок
Интересно почему-то никто не вспомнил, что у функции strpos как бы есть третий параметр, благодаря которому закрывающий тег можно искать сразу за открывающим, а не с самого начала ;)
 

WBS

Новичок
Для каждого варианта сделано 10 испытаний по 500000 повторов в каждом.

1. substr/strpos 1
PHP:
$start = strpos($data0, '<IfIsSlave>') + strlen('<IfIsSlave>'); 
$length = strpos($data0, '</IfIsSlave>') - $start; 
$data = substr($data0, $start , $length);
Среднее время выполнения: 1.564816904068

2. substr/strpos 2
PHP:
$data = substr($data0, $start = strpos($data0,"<IfIsSlave>")+strlen("<IfIsSlave>"), strpos($data0,"</IfIsSlave>")-$start);
Среднее время выполнения: 1.5478840351105

3. substr/stripos
PHP:
$data = substr( $data0, $start = stripos($data0,"<IfIsSlave>")+strlen("<IfIsSlave>"), stripos($data0,"</IfIsSlave>")-$start);
Среднее время выполнения: 28.985386323929

4. preg_match
PHP:
preg_match("/<IfIsSlave>(.*)<\/IfIsSlave>/Ui", $data0, $matches); 
$data = $matches[1];
Среднее время выполнения: 2.7872623682022

Скрипт для теста:

PHP:
<?
set_time_limit(0);

$data0 = '
<?xml version="1.0" encoding="ISO-8859-1"?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> 
<head> 
<title>Object not found!</title> 
<link rev="made" href="mailto:sandel@localhost" /> 
<style type="text/css"><!--/*--><![CDATA[/*><!--*/ 
    body { color: #000000; background-color: #FFFFFF; } 
    a:link { color: #0000CC; } 
    p, address {margin-left: 3em;} 
    span {font-size: smaller;} 
/*]]>*/--></style> 
</head> 

<body> 
<h1>Object not found!</h1> 
<p> 


    The requested URL was not found on this server. 



    If you entered the URL manually please check your 
    spelling and try again. 



</p> 
<p> 
If you think this is a server error, please contact 
the <a href="mailto:sandel@localhost">webmaster</a>. 

</p> 

<IfIsSlave>it\'s just test! ;-)</IfIsSlave> 

<h2>Error 404</h2>
<address> 
  <a href="/">localhost</a><br /> 

  <span>Tue Mar  9 20:37:26 2010<br /> 
  Apache/2.2.13 (Linux/SUSE)</span> 
</address> 
</body> 
</html>
';

$max_i = 500000;
$max_j = 10;

for ($j=0; $j<$max_j; $j++) {
	$time_start = microtime(TRUE);
	for ($i=0; $i<$max_i; $i++) {
		$start = strpos($data0, '<IfIsSlave>') + strlen('<IfIsSlave>'); 
		$length = strpos($data0, '</IfIsSlave>') - $start; 
		$data = substr($data0, $start , $length);
	}
	$time_end = microtime(TRUE);
	$time_result[0][] = $time_end-$time_start;
}

for ($j=0; $j<$max_j; $j++) {
	$time_start = microtime(TRUE);
	for ($i=0; $i<$max_i; $i++)
		$data = substr($data0, $start = strpos($data0,"<IfIsSlave>")+strlen("<IfIsSlave>"), strpos($data0,"</IfIsSlave>")-$start);
	$time_end = microtime(TRUE);
	$time_result[1][] = $time_end-$time_start;
}

for ($j=0; $j<$max_j; $j++) {
	$time_start = microtime(TRUE);
	for ($i=0; $i<$max_i; $i++)
		$data = substr( $data0, $start = stripos($data0,"<IfIsSlave>")+strlen("<IfIsSlave>"), stripos($data0,"</IfIsSlave>")-$start);
	$time_end = microtime(TRUE);
	$time_result[2][] = $time_end-$time_start;
}

for ($j=0; $j<$max_j; $j++) {
	$time_start = microtime(TRUE);
	for ($i=0; $i<$max_i; $i++) {
		preg_match("/<IfIsSlave>(.*)<\/IfIsSlave>/Ui", $data0, $matches); 
		$data = $matches[1];
	}
	$time_end = microtime(TRUE);
	$time_result[3][] = $time_end-$time_start;
}

foreach ($time_result as $tr_id => $tr) {
	$result_table["time_all"][$tr_id] = implode("<br>", $tr);
	$result_table["time_min"][$tr_id] = min($tr);
	$result_table["time_max"][$tr_id] = max($tr);
	$result_table["time_avg"][$tr_id] = array_sum($tr)/sizeof($tr);
}

print("<p><table bgcolor=#000000 cellPadding=5 cellSpacing=1>");
print("<tr bgcolor=#dddddd><td></td><td>substr/strpos 1</td><td>substr/strpos 2</td><td>substr/str<b>i</b>pos</td><td>preg_match</td></tr>");
foreach ($result_table as $key => $rt) {
	if ($key=="time_all")
		$name = "Все испытания";
	elseif ($key=="time_min")
		$name = "MIN";
	elseif ($key=="time_max")
		$name = "MAX";
	elseif ($key=="time_avg")
		$name = "AVG";
	else
		$name = "";
	print("<tr bgcolor=#ffffff><td valign=top>{$name}</td>");
	foreach ($rt as $val)
		print("<td>{$val}</td>");
	print("</tr>");
}
print("</table>");


?>
-~{}~ 10.03.10 15:39:

Автор оригинала: vovanium
Интересно почему-то никто не вспомнил, что у функции strpos как бы есть третий параметр, благодаря которому закрывающий тег можно искать сразу за открывающим, а не с самого начала ;)
Ценное наблюдение ;).

5. substr/strpos 3
PHP:
$data = substr($data0, $start = strpos($data0,"<IfIsSlave>")+strlen("<IfIsSlave>"), strpos($data0,"</IfIsSlave>",$start)-$start);
Среднее время выполнения: 1.2557591438293
 
Сверху