Kirjautuminen

Haku

Tehtävät

Keskustelu: Nettisivujen teko: PHP: \n säännöllisissä lausekkeissa

Sivun loppuun

janijohannes [11.05.2009 20:03:50]

#

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?

Metabolix [11.05.2009 20:27:36]

#

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].

janijohannes [11.05.2009 20:32:45]

#

Kiitos. Tosin omassa koodissani nuo tuplahakaset on ainoastaan yhdellä.
Ja ei ole.

janijohannes [12.05.2009 13:21:59]

#

Nyt se ei toimi kun on lopetuksena /is ...

Metabolix [12.05.2009 21:02:42]

#

Varmaan kyllä vika on jossain muualla kuin siinä lopetuksessa.

jimi-kimi [12.05.2009 22:31:19]

#

<?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);
}

Metabolix [12.05.2009 22:41:07]

#

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.

jimi-kimi [12.05.2009 23:45:10]

#

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.

Metabolix [13.05.2009 00:43:10]

#

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.
?>

jimi-kimi [13.05.2009 00:48:58]

#

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>

jimi-kimi [13.05.2009 02:38:16]

#

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ä.

Metabolix [13.05.2009 10:09:08]

#

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ä)?

janijohannes [13.05.2009 16:58:00]

#

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; }

jimi-kimi [13.05.2009 21:51:51]

#

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


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta