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: 13 let
|
0