Jump to content

Unexected result with preg_replace()


leeming

Recommended Posts

Im trying to create a copy of BBS codes.

 

i know i can do replace on these type of tags, but i have come across an error, and would like to understand it.

 

  //BOLD text
  $tag = '/(\[b\])(.*)(\[\/b\])/';
  $html = '<b>${2}</b>';
  $text = preg_replace($tag, $html, $text);

 

The problem i have with the above is...

 

[b]bold[/b] normal [b]bold[/b]

 

Shows as:

bold[ /b ] norma [ b ]bold

Just without the obvious spaces

 

 

Basicly, it is catching the 1st bold tag, and matching it with the last one it can find (thus the middle is missed out)

Link to comment
Share on other sites

here

 


<?php
$string = 'Hello [b] how are you [/b] sorry';
$pattern[] = '/\[b\](.*)\[\/b\]/';
$replacement[] = '<B>${1}</B>';
echo preg_replace($pattern, $replacement, $string);
?> 

 

EDIT:

this will catch [b] how are you [/b] and replace with <B>(.*)</B> in this case <B>how are you</B>

no need to capture the [b]'s by them selfs as your replacing them..

i added an extended one below just for fun ;D

 

 

i have been playing with RegEx and a may write my own BBCode when time permits sounds like good pratice :D

Link to comment
Share on other sites

extended

 


<?php
$string = 'Hello [b] how are you [i] how are you [u] how are you [/u][/i][/b] sorry';
$pattern[] = '/\[b\](.*)\[\/b\]/';
$pattern[] = '/\[i\](.*)\[\/i\]/';
$pattern[] = '/\[u\](.*)\[\/u\]/';
$replacement[] = '<B>${1}</B>';
$replacement[] = '<I>${1}</I>';
$replacement[] = '<U>${1}</U>';
echo preg_replace($pattern, $replacement, $string);
?> 

Link to comment
Share on other sites

The way you want this to work requires (as far as I know) you to individually locate each one.  Lucky enough for you, I have already written a script that can do this, and it works pretty well as far as I can tell.

 

<?php
class bbcode {

/**
 * List of supported tags
 *
 */
var $_tagList = array(
	'b'		=> '<b>{TEXT}</b>',
	'u'		=> '<u>{TEXT}</u>',
	'i'		=> '<i>{TEXT}</i>',
	'color'	=> '<span style="color: {OPT}">{TEXT}</span>'
);

/**
 * List of tags with an optional attribute (so you can make [color=whatever]sdf[/color] for example
 *
 */
var $_attrList = array(
	'color'	=> "((#[0-9A-F]{3,6})|([A-Z\-]+))"
);

/**
 * Convert from a standard input to HTML
 *
 * @param string $input
 * @return string
 */
function toHTML($input) {

	// Loop through the taglist and convert all
	foreach ( $this->_tagList as $tagKey=>$tagHTML ) {
		$tagOpen = "[{$tagKey}]";
		$tagClose = "[/{$tagKey}]";

		// Find the first instance of the current tag item
		if (!$this->_attrList[$tagKey]) {
			while ((stripos($input, $tagOpen) !== FALSE) && (strripos($input, $tagClose) !== FALSE)) {
				$position = stripos($input, $tagOpen);
				if ($position === FALSE) continue;
				$endposition = strripos($input, $tagClose, $position);
				if ($endposition === FALSE) continue;

				// Replace the text
				$newText = substr($input, $position + strlen($tagOpen), $endposition - ($position + strlen($tagOpen)));
				$newText = str_replace("{TEXT}", $newText, $tagHTML);
				$input = substr($input, 0, $position) . $newText . substr($input, $endposition + strlen($tagClose));
			}
		} else {
			$tagOpen = "@\[{$tagKey}=".$this->_attrList[$tagKey]."\]@is";
			while (preg_match($tagOpen, $input) && (strripos($input, $tagClose) !== FALSE)) {
				preg_match($tagOpen, $input, $match);
				$position = stripos($input, $match[0]);
				if ($position === FALSE) continue;
				$endposition = strripos($input, $tagClose, $position);
				if ($endposition === FALSE) continue;

				// Replace the text
				$newText = substr($input, $position + strlen($match[0]), $endposition - ($position + strlen($match[0])));
				$tagHTML = str_replace("{OPT}", $match[1], $tagHTML);
				$newText = str_replace("{TEXT}", $newText, $tagHTML);
				$input = substr($input, 0, $position) . $newText . substr($input, $endposition + strlen($tagClose));
			}
		}
	}
}
}
?>

I haven't really optimized this code yet, but it works.  Anyways, I think you can figure out how it works, if not, let me know.

Link to comment
Share on other sites

I thank you for the help, but... does not fix any thing...

 

my code still takes the 1st and last (ignoring middle tags) and applying formatting to the whole lot. But i may add, this seems to be only in sections (maybe lines?)

 

I am not fimilar with the syntax, which is why i was enclosing the [ b ] tags (i got hold of the bbs for [ url ] and changed it to do the rest of the tags for myself)

Link to comment
Share on other sites

The way you want this to work requires (as far as I know) you to individually locate each one.  Lucky enough for you, I have already written a script that can do this, and it works pretty well as far as I can tell.

 

<?php
class bbcode {

/**
 * List of supported tags
 *
 */
var $_tagList = array(
	'b'		=> '<b>{TEXT}</b>',
	'u'		=> '<u>{TEXT}</u>',
	'i'		=> '<i>{TEXT}</i>',
	'color'	=> '<span style="color: {OPT}">{TEXT}</span>'
);

/**
 * List of tags with an optional attribute (so you can make [color=whatever]sdf[/color] for example
 *
 */
var $_attrList = array(
	'color'	=> "((#[0-9A-F]{3,6})|([A-Z\-]+))"
);

/**
 * Convert from a standard input to HTML
 *
 * @param string $input
 * @return string
 */
function toHTML($input) {

	// Loop through the taglist and convert all
	foreach ( $this->_tagList as $tagKey=>$tagHTML ) {
		$tagOpen = "[{$tagKey}]";
		$tagClose = "[/{$tagKey}]";

		// Find the first instance of the current tag item
		if (!$this->_attrList[$tagKey]) {
			while ((stripos($input, $tagOpen) !== FALSE) && (strripos($input, $tagClose) !== FALSE)) {
				$position = stripos($input, $tagOpen);
				if ($position === FALSE) continue;
				$endposition = strripos($input, $tagClose, $position);
				if ($endposition === FALSE) continue;

				// Replace the text
				$newText = substr($input, $position + strlen($tagOpen), $endposition - ($position + strlen($tagOpen)));
				$newText = str_replace("{TEXT}", $newText, $tagHTML);
				$input = substr($input, 0, $position) . $newText . substr($input, $endposition + strlen($tagClose));
			}
		} else {
			$tagOpen = "@\[{$tagKey}=".$this->_attrList[$tagKey]."\]@is";
			while (preg_match($tagOpen, $input) && (strripos($input, $tagClose) !== FALSE)) {
				preg_match($tagOpen, $input, $match);
				$position = stripos($input, $match[0]);
				if ($position === FALSE) continue;
				$endposition = strripos($input, $tagClose, $position);
				if ($endposition === FALSE) continue;

				// Replace the text
				$newText = substr($input, $position + strlen($match[0]), $endposition - ($position + strlen($match[0])));
				$tagHTML = str_replace("{OPT}", $match[1], $tagHTML);
				$newText = str_replace("{TEXT}", $newText, $tagHTML);
				$input = substr($input, 0, $position) . $newText . substr($input, $endposition + strlen($tagClose));
			}
		}
	}
}
}
?>

I haven't really optimized this code yet, but it works.  Anyways, I think you can figure out how it works, if not, let me know.

 

The class looks impressive but, i would like to add that this code i have custom tags, due it been made for some one (its part of my coursework, even though i use php quite a bit any way). And also i'd have to document all this external code. (like previously said, im wanting to understand why it is doing this... i can still use str_replace)

Link to comment
Share on other sites

Well you can add as many as you want to the array assuming the tags are formed such as: [b ].  While that code isn't exactly documented, you can feel free to use it if you want, or, you can just check out how I used stripos and strripos, but this might not give you the desired output.

 

This would take:

Some [b]sample[/b] [b]string[/b]

And make it:

Some <b>sample[/b] [b] string</b>

 

As stated, I haven't really fixed this function perfectly...But the reason it works as shown above is because that's how I intended it to work in order to deal with tags correctly.

Link to comment
Share on other sites

...

This would take:

Some [b]sample[/b] [b]string[/b]

And make it:

Some <b>sample[/b] [b] string</b>

...

 

exactly what im trying to explain...

 

oh well, i will just use a simple string replace, and still wonder why i get that error :P

Link to comment
Share on other sites

Nope i am now forced to do this function for one of my custom tags

 

  
//References
  $tag = '/\[ref\](.*)\[\/ref\]/';
  $html = '<sup><a href="#ref${1}">[${1}]</a></sup>';
  $text = preg_replace($tag, $html, $text);

 

It is an page to add articles, and [ref] just anchors to the bottom where the references are all listed (by number)

 

i get the same annoying output as before...

 

text [ref]1[/ref] middle [ref]2[/ref] end

 

which outputs

 

text <sup><a href="#ref1[/ref] middle [ref]2">[1[/ref] middle [ref]2]</a></sup> end

 

is there no way to get preg_replace (or similar function) to go thru left to right instead of skipping to the last tag?

Link to comment
Share on other sites

if its just numbers then you could use

 

<?php
$string = 'text [ref]1[/ref] middle [ref]2[/ref] end';
$pattern[] = '/\[ref\]([\d]*)\[/ref\]/';
$replacement[] = '<sup><a href="#ref${1}">[${1}]</a></sup>';
echo preg_replace($pattern, $replacement, $string);
?>

Link to comment
Share on other sites

oh yeah if the ref's are single words (no spaces) this should work

 

 

 

<?php
$string = 'text [ref]1[/ref] middle [ref]2[/ref] end';
$pattern[] = '/\[ref\](\w*)\[\/ref\]/';
$replacement[] = '<sup><a href="#ref${1}">[${1}]</a></sup>';
echo preg_replace($pattern, $replacement, $string);
?>

Link to comment
Share on other sites

Guest prozente

use the ungreedy modifier

 

<?php
$text = 'text [ref]1[/ref] middle [ref]2[/ref] end';
$tag = '/\[ref\](.*)\[\/ref\]/U';
$html = '<sup><a href="#ref${1}">[${1}]</a></sup>';

$text = preg_replace($tag, $html, $text);

?>

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.