| Autor | Zpráva | ||
|---|---|---|---|
| Tori Profil |
#1 · Zasláno: 2. 8. 2011, 19:34:19
Pěkný večer.
Prosím o radu, jak zlepšit tuto funkci. Funguje správně, ale mám pocit, že by šla napsat i efektivněji (a vůbec je moc dlouhá). Díky moc za ochotu. /**
* @param string $text Vstupni text
* @param array $open Zacatecni tag. array([search], [replace])
* @param array $close Ukoncovaci tag. array([search], [replace])
* @param string $tagName Jmeno tagu (aby se nemusel rozebirat regular).
* @return string
*/
function replaceNestedTags($text, $openTag, $closeTag, $tagName) {
// Zjisti pocet tagu a offsety
preg_match_all('~'.str_replace('~','\~',$openTag['search']).'~i', $text, $matchesOpen, PREG_OFFSET_CAPTURE | PREG_PATTERN_ORDER);
preg_match_all('~'.str_replace('~','\~',$closeTag['search']).'~i', $text, $matchesClose, PREG_OFFSET_CAPTURE | PREG_PATTERN_ORDER);
$found = array_merge($matchesOpen[0], $matchesClose[0]);
if (empty($found))
return $text;
$queue = array();
foreach ($found as $item)
$queue[$item[1]] = $item[0];
ksort($queue);
// Oprav neuzavrene tagy
$depth = 0;
$offsetFix = 0;
foreach($queue as $offset=>$tag) {
$level = substr($tag,1,1) == '/' ? -1 : 1; // Koncovy nebo zacatecni tag?
if ($depth == 0 && $level == -1) {
$text = substr_replace($text, '', $offset-$offsetFix, $ln = strlen($tag));
$offsetFix += $ln;
continue;
}
$depth += $level;
}
if ($depth > 0)
$text = substr_replace($text, str_repeat("[/$tagName]", $depth), $offset-$offsetFix+strlen($tag), 0);
// Nahrazeni tagu HTML
$text = preg_replace('~'.str_replace('~','\~',$openTag['search']).'~i', $openTag['replace'], $text);
$text = preg_replace('~'.str_replace('~','\~',$closeTag['search']).'~i', $closeTag['replace'], $text);
return $text;
}$openTag = array('search'=>'\[quote(?:=([^\]]+))?\]', 'replace'=>'<div class="quote"><b>Citace $1</b><br>');
$closeTag = array('search'=>'\[/quote\]', 'replace'=>'</div>');
$text = replaceNestedTags($text, $openTag, $closeTag, 'quote'); |
||
| peta Profil |
#2 · Zasláno: 3. 8. 2011, 08:28:43 · Upravil/a: peta
Tori: Rozumis te funkci? Prevod znacek jsou na radku (1). To ostatni resi otevrene a uzavrene tagy a snazi se je doplnit. Nahrazeni tagu muzes udelat i pres str_replace (2), http://cz.php.net/str_replace. Samotne nahrazeni by slo napsat jako (3).
(1)
// Nahrazeni tagu HTML
$text = preg_replace('~'.str_replace('~','\~',$openTag['search']).'~i', $openTag['replace'], $text);
$text = preg_replace('~'.str_replace('~','\~',$closeTag['search']).'~i', $closeTag['replace'], $text);
return $text;
(2)
a)
$bodytag = str_replace("%body%", "<body>", "%html%%body%");
b)
$search = array("[quote]", "[/quote]","", ""); // MODERATOR? Rozbils to. je tam " [ b ] "," [ / b ]" (bez mezer, proste b tag)
$replace = array("<div class=\"quote\">", "</div>", "<b>", "</b>");
$text = "You should eat fruits, vegetables, and fiber every day.";
$text = str_replace($search, $replace, $text);
(3)
$text = preg_replace('\[quote(?:=([^\]]+))?\]','<div class="quote"><b>Citace $1</b><br>', $text);
$text = preg_replace('\[/quote\]', '</div>', $text); |
||
| Tori Profil |
#3 · Zasláno: 3. 8. 2011, 08:57:30
peta:
Ale jistě, že bych mohla řešit nahrazování jedním nebo dvěma reguláry. Původně se to i na tom fóru používalo, jenže: - regulár [tag](.+?)[/tag] neřeší vnořené tagy, což se hlavně u citací hodně používá. - samostatný převod [tag] => <div>, [/tag] => </div> neřeší neuzavřené tagy, a pár </div> navíc může nadělat docela paseku. - rekurzivní zpracování regulárem [tag](.+?)[/tag] rovněž neo(d)praví neuzavřené tagy. Proto jsem to zkoušela udělat takhle. Možná by šlo použít rekurzi a pak odmazat všechny přebývající začáteční/koncové tagy, ale nezkoušela jsem to. Celkově mi rekurzivní zpracovávání regulárem přišlo míň efektivní než 2x vyhledání + 2x nahrazení (+výjimečně pár str_replace). Na druhou stranu, pokud by se to aplikovalo na jednotlivé příspěvky o průměrně 10 řádcích, tak ten rozdíl asi nebude tak velký. Asi to pro jistotu taky zkusím. |
||
|
Časová prodleva: 3 dny
|
|||
| Alphard Profil |
#4 · Zasláno: 6. 8. 2011, 02:08:55
Tori:
Přesně uvedené chování asi moc zjednodušit nejde, nebo mě žádné přímé zkrácení nenapadá. Podobného výsledku jde dosáhnout typickým replace [>b]neco[>/b] na <b>neco</b>, jak sama píšeš. Co zbyde se buď nechá jako BB, nebo smaže. Délku kódu bych neřešil, bude schovaný v nějaké knihovně, rychlejší je lepší.
function replaceNestedTags($text, $openTag, $closeTag, $tagName)
{
$c = 0;
do
{
$text = preg_replace(
"~$openTag[search](.*)$closeTag[search]~U",
"$openTag[replace]\\2$closeTag[replace]", // tady \\2 bohužel nebude fungovat obecně
$text,
-1,
$c
);
}
while ($c > 0);
$text = preg_replace("~$openTag[search]|$closeTag[search]~", '', $text);
return $text;
}Stejně je ale problém s křížením tagů, nevyrovná se s ním ani tato diskuse Hřiště. Ale zvládá to třeba Texy! http://texy.info/cs/try/jgkpq. |
||
| Majkl578 Profil |
#5 · Zasláno: 6. 8. 2011, 04:44:12
Pokud by šlo o to jak to napsat pěkněji, tak by to šlo třeba takto (rekurzí):
function replaceNestedTags($text, $openTag, $closeTag)
{
return preg_replace_callback('~' . str_replace('~', '\~', $openTag[0]) . '(.+)' . str_replace('~', '\~', $closeTag[0]) . '~is',
function (array $m) use ($openTag, $closeTag) {
return sprintf($openTag[1], $m[1]) . replaceNestedTags($m[2], $openTag, $closeTag) . $closeTag[1];
},
$text
);
}
$text = <<<EOS
[quote]
[quote=Tori]
Lorem ipsum.
[/quote]
Dolor sit amet.
[/quote]
blablabla
EOS;
replaceNestedTags(
$text,
['\[quote(?:=([^\]]+))?\]', '<div class="quote"><b>Citace %s</b><br>'],
['\[/quote\]', '</div>']
);Má ale smysl řešit rychlost na úkor přehlednosti? Stejně je žádoucí, aby se zkompilovaná verze kešovala. |
||
| Alphard Profil |
#6 · Zasláno: 6. 8. 2011, 10:40:38 · Upravil/a: Alphard
Majkl578:
„bohužel mi ale nefunguje (místo HTML je nic)“ Píšeš to, jako by to byl detailní problém :-) Já to trochu testoval a šlo to, možná nějaká změna v nové verzi PHP? Jen jsem tam nedal dost parametrů na víceřádkový vstup. function replaceNestedTags($text, $openTag, $closeTag) {
$c = 0;
do
{
$text = preg_replace(
"~$openTag[search](.*)$closeTag[search]~siU",
"$openTag[replace]\\2$closeTag[replace]",
$text,
-1,
$c
);
}
while ($c > 0);
$text = preg_replace("~$openTag[search]|$closeTag[search]~", '', $text);
return $text;
}
$text = <<<EOS
[quote]
[quote=Tori]
Lorem ipsum.
[/quote]
Dolor sit amet.
[/quote]
blablabla
EOS;
$openTag = array('search'=>'\[quote(?:=([^\]]+))?\]', 'replace'=>'<div class="quote"><b>Citace $1</b><br>');
$closeTag = array('search'=>'\[/quote\]', 'replace'=>'</div>');
$text = replaceNestedTags($text, $openTag, $closeTag, 'quote');<div class="quote"><b>Citace </b><br> <div class="quote"><b>Citace Tori</b><br> Lorem ipsum. </div> Dolor sit amet. </div> blablabla Nemám teda escapované ~, ale to už by se doladilo. |
||
| Majkl578 Profil |
#7 · Zasláno: 6. 8. 2011, 15:35:51 · Upravil/a: Majkl578
[#6] Alphard: No jasně, modifikátor
s to vyřešil, nenapadlo mě kouknout se na to rovnou.
Znovu jsem to porovnal tímto scriptem: $m = microtime(TRUE);
for($i = 0; $i < 10000; $i++) {
$openTag = ['\[quote(?:=([^\]]+))?\]', '<div class="quote"><b>Citace %s</b><br>'];
$closeTag = array(['\[/quote\]', '</div>']);
replaceNestedTagsM($text, $openTag, $closeTag);
}
echo microtime(TRUE) - $m;
echo "\n";
$a = microtime(TRUE);
for($i = 0; $i < 10000; $i++) {
$openTag = ['search'=>'\[quote(?:=([^\]]+))?\]', 'replace'=>'<div class="quote"><b>Citace $1</b><br>'];
$closeTag = ['search'=>'\[/quote\]', 'replace'=>'</div>'];
replaceNestedTagsA($text, $openTag, $closeTag, 'quote');
}
echo microtime(TRUE) - $a;
echo "\n";
$t = microtime(TRUE);
for($i = 0; $i < 10000; $i++) {
$openTag = ['search'=>'\[quote(?:=([^\]]+))?\]', 'replace'=>'<div class="quote"><b>Citace $1</b><br>'];
$closeTag = ['search'=>'\[/quote\]', 'replace'=>'</div>'];
replaceNestedTagsT($text, $openTag, $closeTag, 'quote');
}
echo microtime(TRUE) - $t;A vychází mi: replaceNestedTagsM 0.5869s (moje verze) replaceNestedTagsA 0.9700s (Alphardova verze) replaceNestedTagsT 1.8297s (původní verze z [#1]) Jo a nenechte se zmást zápisem pole jako [...], to je novinka v PHP 5.4.
|
||
| Alphard Profil |
#8 · Zasláno: 6. 8. 2011, 17:56:12 · Upravil/a: Alphard
To by mě zajímalo, jak jsi to naměřil. Mně to na PHP 5.3.6 vyšlo jinak.
Nejdřív jsem otestoval funkčnost všech řešení: // zvýrazňovač to blbě formátuje, tak to sem dávat nebudu, všude je to pro daný text ok a pak jsem zvýšil iterace na 1e5 (ať neřešíme desetiny) a otestoval čas: >php bbCode.php Majkl: 3.2695510387421 Alphard: 2.4737310409546 Tori: 3.9173190593719 >Exit code: 0 Time: 12.245 >php bbCode.php Majkl: 3.2842628955841 Alphard: 2.6889259815216 Tori: 4.1712620258331 >Exit code: 0 Time: 12.757 >php bbCode.php Majkl: 3.3238320350647 Alphard: 2.6214458942413 Tori: 3.9816880226135 >Exit code: 0 Time: 12.438 Kompletní zdroják. Prakticky je jedno, která verze se použije, spíš by mě zajímalo, proč jsi to naměřil jinak, že by PHP 5.4? |
||
| Majkl578 Profil |
#9 · Zasláno: 6. 8. 2011, 18:06:54
Aha, mně se tam totiž vloudila jedna chybička, která to zkreslila a které jsem si nevšiml (copy&paste je zlo):
$closeTag = array(['\[/quote\]', '</div>']); má být $closeTag = ['\[/quote\]', '</div>'];
Tak to pardon. Jinak ještě výsledky tvého scriptu z Pastebinu pro 1e5 iterací: $ php /www/temp.php Majkl: 16.467011928558 Alphard: 11.874949932098 Tori: 17.485752820969 $ php -v PHP 5.4.0beta1-dev (cli) (built: Aug 3 2011 21:53:58) (DEBUG) |
||
| Tori Profil |
#10 · Zasláno: 7. 8. 2011, 09:01:33
Alphard, Majkl578:
No páni, děkuju moc za pomoc i inspiraci :) Na tom fóru použiju nakonec Alphardovu verzi (kvůli rychlosti + PHP 5.2 na serveru). |
||
|
Časová prodleva: 14 let
|
|||
0