Keksiikö joku, mitä pitäisi tehdä, kun tarvitsisi saada BBCODE:ssa rivinvaihto toimimaan tagien sisällä?
Seuraava toimii:
$txt=preg_replace("/\[$bb\](.*?)\[\/{$bb2[0]}\]/i",$re,$txt);
Mutta seuraava ei:
$txt=preg_replace("/\[$bb\](.*?[\\n])\[\/{$bb2[0]}\]/i",$re,$txt);
Eli mikä tässä on mennyt pieleen?
Toimivassakin koodissasi on ongelma: et ole merkinnyt \-merkkejä kahtena, joten \[ toimii vain siitä onnesta, että PHP ei tunnista sitä kelvolliseksi escape-merkinnäksi. Kirjoita siis "\\[", jotta \\ tulkitaan \-merkiksi eikä PHP;n tarvitse edes arvailla, olisiko \[ jotain erityistä. Funktiolle päätyy teksti, jossa on merkit \ ja [, ja näistä taas funktio päättelee edelleen, mitä kuuluukin. Sama pätee muihinkin merkintöhin, mm. "\/" => "\\/".
Jostain kummallisesta syystä olet silti yrittänyt kirjoittaa rivinvaihdon tällä mainitulla tavalla, mikä on sikäli väärin, että tavoitteenasi on kuitenkin osua juuri rivinvaihtomerkkiin "\n" etkä erillisiin merkkeihin \ ja n, jotka merkinnästä "\\n" tulee. Ja tämänkään korjauksen jälkeen koodi ei toimi, koska säännölliset lausekkeet oletusarvoisesti käsittelevät yhtä riviä.
Lisää i-valitsimen kaveriksi s-valitsin, niin pääset tästä pulmasta — tällöin piste kelpuuttaa myös rivinvaihdon eikä sitä tarvitse edes erikseen merkitä. (Saa sen toki laittaa, jos haluaa.) Kannattaa muistaa myös, että rivinvaihtona voi olla ja usein onkin "\r\n".
Itse käytin koodivinkissäni C++-koodin värityksestä säännöllistä lauseketta "(?>\r\n|\r|\n)" rivinvaihdon tunnistamiseen. Tällä löytyvät Windowsin, Unixin ja Macin rivinvaihdot. Sulut rajaavat |-operaattorin vaikutuksen, ja ?>-merkinnän merkityksen voit selvittää jostakin itse.
Ai niin, yrityksessäsi omituista on myös, että haluat rivinvaihdon juuri vain tagin loppuun etkä edes ehdollisena. Onko tarkoituksesi tosiaan, että tagit pitää kirjoittaa näin?
Tässä on [b]lihavointi [/b] ja [i]kursivointi [/i].
Kiitos. Tosin omassa koodissani nuo tuplahakaset on ainoastaan yhdellä.
Ja ei ole.
Nyt se ei toimi kun on lopetuksena /is ...
Varmaan kyllä vika on jossain muualla kuin siinä lopetuksessa.
<?php function parse_usercode($input){ $bb = array("/\[b\](.+?)\[/b\]/s"); $markup = array("<b>$1</b>"); return preg_replace($bb, $markup, $input); } ?>
EDIT: Lisätty <?php ?>
EDIT2: </b>
Jotenkin tohon malliin pitäisi toimia, en kuitenkaan ole testannut tuota.
EDIT3:
Jäin miettimään, että olisiko seuraava tapa kuintenkin parempi. Joku enemmän asioista tietävä voisi valaista asiaa.
<?php function parse_usercode($input){ $bb = array("/\[b\]/", "/\[/b\]/"); $markup = array("<b>", "</b>"); return preg_replace($bb, $markup, $input); }
Tagien huono puoli on, että käyttäjät saavat niillä aikaan epävalidia koodia sulkemalla niitä väärässä järjestyksessä. Voisi olla mielenkiintoinen ajatus, että pelkkä tyhjä [/] aina sulkisi viimeksi avatun tagin. Tällä tavalla ei tarvitsisi kuin pitää kirjaa avaustageista ja korvata sulkutagit oikein. Tämän olisi todella helppo toteuttaa preg_replace_callback-funktiolla, ja sisäkkäiset tagitkin toimisivat heti oikein.
Tälläisen kyhäsin nopeasti php.netin preg_replace_callback ohjeen kanssa. Kommentteja ratkaisusta otan mieluusti vastaan, koska itsekkin painin samankaltaisen ongelman parissa.
Tuo mystinen $hits[0] on tässä tapauksessa
[b]lihavoitu[/][i]kursiivi[/]
<?php $usercode = "[b]lihavoitu[/]\n"; $usercode .= "[i]kursiivi[/]\n"; function parse_tag($hits){ $bbcode = array( '/\[(b)\](.+?)\[.*?\]/s', '/\[(i)\](.+?)\[.*?\]/s' ); $markup = array( '<b>$2</$1>', '<i>$2</$1>' ); return preg_replace($bbcode, $markup, $hits[0]); } print preg_replace_callback('/(\[.*?\])(.+?)(\[\/\])/s',"parse_tag",$usercode); ?>
EDIT:
Koodin virheellisyyden ja asian mielenkiinnon takia näpertelin seuraavaa joka saattaa jopa toimia :) Metabolixin ja tuon edellä mainitun php.net sivun kautta sain ymmärrystä ja tietoa asian tiimoilta :)
<?php $usercode = "[b]lihavoitu"; $usercode .= "[b][i]laalaa[/]lihavointi[/][/]"; $usercode .= "[i]kursiivi[/]"; function parse_tag($hits){ $bbcode = array( '/\[(i)\](.+?)\[\/\]/s', '/\[(b)\](.+?)\[\/\]/s' ); $markup = array( '<i>$2</$1>', '<b>$2</$1>' ); return preg_replace($bbcode, $markup, $hits[0]); } while($usercode != preg_replace_callback('/(\[.*?\])(.+?)(\[\/\])/s',"parse_tag",$usercode)){ $usercode = preg_replace_callback('/(\[.*?\])(.+?)(\[\/\])/s',"parse_tag",$usercode); } print $usercode; ?>
On tuossa vielä muutama rivi muokattavaa. Melkeimpä kannattaisi käyttää create_function funktiota tuon parse_tagin tilalla, jotta koodin selkeys säilyisi / tulisi epäselvempää :) Näin asiasta poiketen, viitsisikö joku heittää hyvien kirjojen nimiä / linkkejä asian tiimoilta.
Ei, et tuolla ratkaissut lainkaan niitä ongelmia, joihin tarjosin kyseistä menetelmää ratkaisuksi. (Edit. Jälkimmäisellä melkein, mutta sekin taitaa sulkea sisäkkäiset tagit väärässä järjestyksessä?)
Tarkoitin tällaista:
<?php function avaa_tagi($t) { # tarkistukset ja lisäparsinta tänne return "<$t>"; } function sulje_tagi($t) { # tarkistukset tänne return "</$t>"; } function loytyi_tagi($osuma) { static $auki = array(); # tuki tagiin kuulumattomille [- ja ]-merkeille if ($osuma[0] == '[(]') return '['; if ($osuma[0] == '[)]') return ']'; # jos osui tagiin if (isset($osuma[1])) { $tagi = $osuma[1]; # sulkutagi if ($tagi == '/') { # suljetaan viimeisin $sulje = array(array_pop($auki)); } elseif ($tagi[0] == '/') { # suljetaan kaikki viimeiset, # kunnes saadaan oikea suljettua $tagi = substr($tagi, 1); if (!in_array($tagi, $auki)) { return ''; } $sulje = array(); while (($sulje[] = array_pop($auki)) != $tagi); } } else { # teksti loppui, suljetaan kaikki if (empty($auki)) return ''; $tagi = ''; $sulje = array_reverse($auki); $auki = array(); } if (empty($sulje)) { # merkitään avatuksi; poimitaan nimi alusta preg_match("/^[0-9A-Za-z_]+/", $tagi, $nimi); $auki[] = $nimi[0]; # avataan tagi return avaa_tagi($tagi); } else { # suljetaan suljettavat $ret = ''; foreach ($sulje as $tagi) { $ret .= sulje_tagi($tagi); } return $ret; } } $teksti = preg_replace_callback('/\\[(.+?)\\]|$/s', 'loytyi_tagi', $teksti); # "Moi, [b]Matti[/], [em]hassu [i]otus[/em]" # "Moi, <b>Matti</b>, <em>hassu <i>otus</i></em>" # Tässä b-tagi suljettiin [/]-merkinnällä ja i-tagi automaattisesti, # kun ulompi em-tagi loppui. ?>
Mm.. Mielestäni tuo jälkimmäinen alempi looppinsa kanssa sulkee tagit ihan oikein, jos nyt mitään käsitin:
<b>lihavoitu<b><i>laalaa</i>lihavointi</b></b><i>kursiivi</i>
Saattaa olla aika turha postaus, mutta postaan kuitenkin.. Ainakin seuraava koodi tekee saman, mitä tuo sinunkin koodisi testaamallani tagirimpsulla.
rimpsu:
<? $usercode = "[b]a[b]a[i]a[b]a[/]a[i]a[/]a[/]a[/]a[/]a[i]a[b]a[/]a[i]a[/]a[/][i]a[/][h1]1[h2]2[h3]2[h4]4[/][/][/][/]"; function parse_tag($hits){ $bbcode = array( '/\[([a-z0-9]+)\](.+?)\[\/\]/s' ); $markup = array( '<$1>$2</$1>' ); return preg_replace($bbcode, $markup, $hits[0]); } while($usercode != preg_replace_callback('/(\[.+?\])(.+?)(\[\/\])/s',"parse_tag",$usercode)){ $usercode = preg_replace_callback('/(\[.+?\])(.+?)(\[\/\])/s',"parse_tag",$usercode); } print $usercode; ?>
Ja tulostukset:
Ylläoleva viritykseni: <b>a<b>a<i>a<b>a</b>a<i>a</i>a</b>a</i>a</b>a<i>a<b>a</i>a<i>a</i>a</b><i>a</i><h1>1<h2>2<h3>2<h4>4</h1></h2></h3></h4> Metabolixin: <b>a<b>a<i>a<b>a</b>a<i>a</i>a</b>a</i>a</b>a<i>a<b>a</i>a<i>a</i>a</b><i>a</i><h1>1<h2>2<h3>2<h4>4</h1></h2></h3></h4>
Kysynkin siis onko jompikumpi parempi ja jos on, niin miksi?
EDIT: Niin ja tiedän että merkkijonokin riittäisi taulukon sijasta.. --> nukkumahan
EDIT: Ei päässyt vielä nukkumaan, seuraavaa oli pakko koittaa vielä tieteen ja taiteen nimissä:
$starttime = microtime(); .. $endtime = microtime(); print $endtime - $starttime;
Oma: 0.000486
Metabolix: 0.000348
ja nämä ovat siis keskiarvotuloksia tällä vanhalla rakkineella.
Siirtyminen taulukoista merkkijonoihin paransi hieman aikaa: 0.000436
Aamuyön viimeinen edit:
$usercode = "[b]a[b]a[i]a[b]a[/]a[i]a[/]a[/]a[/]a[/]a[i]a[b]a[/]a[i]a[/]a[/][i]a[/][h1]1[h2]2[h3]2[h4]4[/][/][/][/]";
Sadalla rivillä tuota nopeuserot olivat jo huomattavat, mutta Metabolixin esimerkki ei suostunut parsimaan viimeistä paria kymmentä riviä.
Jos otat aikaa, aja korvaus niin monta kertaa, että ajasta tulee edes sekunnin kymmenyksiä. Pienemmässä mittakaavassa tulos on paljon tuurista kiinni.
Koodisi ei toimi oikein. Et ehkä lukenut tulostetta riittävän tarkasti.
Tässä siitä esimerkki:
[a]moi[b][c][/][/][/] <a>moi<b><c></a></b></c>
Sulkemisten pitäisi tietenkin olla juuri käänteisessä järjestyksessä:
<a>moi<b><c></c></b></a>
Saman huomaat hyvin omankin viimeisen esimerkkisi lopun h-tageista.
Muokkasin aiempaan koodiini tagin nimen tunnistukseen [a-z] => [0-9A-Za-z_], jotta se toimisi muillakin kuin vain pieniä kirjaimia sisältävillä tageilla (esim. h1-h4 esimerkissäsi).
Mitä koodini ei suostunut parsimaan? :o Voitko antaa esimerkin syötteestä (tai generointiskriptistä)?
Tietääkseni tuollainen tarkistus hoidetaan jo funktiossani... ei tosin mitään hajua.
Annan tämän funktioni vielä tähän jos joku osaa kertoa vian. (Kannattaisikohan minun käyttää nl2br-funktiota?):
<?php function BBCODE($txt) { $ret=array( 'b' => '<em>\1</em>', 'i' => '<i>\1</i>', 'u' => '<span style="text-decoration: underline;">\1</span>', 'code' => '<code>\1</code>', 'url' => '<a href="\1">\1</a>', 'url\=(.*?)' => '<a href="\1">\2</a>', 'font\=(.*?)' => '<span style="font-size: \1px">\2</span>', 'img' => '<img src="\1" />'); foreach ($ret as $bb => $re) { $bb2=explode('\=',$bb); $txt=preg_replace('/\['.$bb.'\](.*?)\[\/'.$bb2[0].'\]/is',$re,$txt); } /* BBCODE -> HTML */ return $txt; }
Metabolix: Pikaisen testin tuloksena se näyttää nyt sitten kuitenkin parsivan koodin oikein. Olit muuten oikeassa purkkaviritykseni toimivuudesta. Noh, aina ei voi voittaa ja pienen tutkailun jälkeen jopa ymmärsin esimerkkisi( tai ainakin osan siitä.)
janijohannes: Riittäisikö tämä https://www.php.net/bbcode
Aihe on jo aika vanha, joten et voi enää vastata siihen.