Jump to content

Help with either str_replace or preg_replace


Simon Lloyd

Recommended Posts

Hi all, this is my first post here AND i'm a novice so please be gentle.

I own a forum and my members can post code (much like here) but i used a modified version of CODE tags, some of my members are either new to forums or just dont use the tags, what i'd like is, via php, to check to see if my KODE tags are in place if they are do nothing, if CODE tags are in place replace them with the KODE tags, if neither KODE or Code tags are in place look for Sub anytext(parameters) where anytext can be anything (there's always a space between Sub and anytext) and parameters may or may not be present so those parenthesis could simply look like () .

 

When found replace Sub anytext(parameters) for [KODE]Sub anytext(parameters) then find End Sub and replace with End Sub[KODE]

 

The only other anomalies would be the word Sub could appear as Function

 

I can replace CODE tags if they exist, but cannot do the rest, so it would be great if it was all done in one go, i've heard that str_replace is quicker than preg_replace which of course would be preferrable, here's my effort at replacing the tags one way but it doesn't work.

$pattern[0] = "Sub ";
$pattern[1] = "End Sub"; 
$replace[0] = "[KODE]Sub\s";
$replace[1] = "End\sSub\[\/KODE\]";
$mystring = preg_replace($pattern, $replace, $mystring);

The spaces are intentional, but i didnt know if i should have the whitespace character \s in the patterns.

 

Update:

This works to a fashion, however it doesn't check if any tags already exist and doubles up, not good!

$pattern1 = "/Sub\s/"; 
$pattern2 = "/End Sub/"; 
$pattern3 = "/Function\s/"; 
$pattern4 = "/End Function/";  
$replace1 = "[KODE]Sub "; 
$replace2 = "End Sub[/KODE]"; 
$replace3 = "[KODE]Function "; 
$replace4 = "End Function[/KODE]"; 

$MyString= preg_replace($pattern1, $replace1, $post['message']); 
$MyString= preg_replace($pattern2, $replace2, $post['message']); 
$MyString= preg_replace($pattern3, $replace3, $post['message']); 
$MyString= preg_replace($pattern4, $replace4, $post['message']); 
$MyString= preg_replace('/\\[code\\](.*)\\[\/code\\]/siU', '[KODE]\\1[/KODE]', $post['message']); 

 

I'm sure this can be combined and made more efficient!

 

Regards,

Simon

 

P.S I should mention im using PHP5.2.17

 

Update 2:I have wrapped the whole thing in this

if (strpos($post['message'],'/[KODE]/') !== true AND strpos($post['message'],'/[/KODE]/') !== true ) {
//All the preg_replace code
}

but now the first replacement for "Sub " doesn't seem to take place?

Link to comment
Share on other sites

Hey thanks for the reply, there will never be a Sub anytext(parameters) without End Sub and they are never nested within each other, however they may appear after each other like

Sub Test()
'some code
End Sub
Sub Test2(MyTest by Val)
'some code
End Sub
Function TestFunc()
'some code
End Function
Function TestFunc2(Rng As Range)
'some code
End Function

the code should skip replacing Sub or Function if the [KODE] tag is present before it the same with End Sub or End Function [/KODE] tag appearing after them should cause it to be skipped from replacing, there would only be one strange anomaly and that is the word Function may appear within a sub like

Sub Mytest()
'some code
Function...etc
End Sub

so if the word function appears between Sub... and End Sub then it should not be replaced, it never happens the other way around like Sub appearing in the Function routine.

 

Lol, have i made any sense? ask me anything else you need, i appreciate anything you can help with :)

Link to comment
Share on other sites

This should give you a good start

 

<?php

$expr = '/(\[kode\])?\s*(sub|function)\s+([a-z0-9]+)\(([a-z0-9 ]*)\)(.*?)end \2/si';
$data = "Sub Test()
'some code
End Sub
[KODE]Sub Test2(MyTest by Val)
'some code
End Sub[/KODE]
Function TestFunc()
'some code
End Function
Function TestFunc2(Rng As Range)
'some code
End Function
[KODE]
Function preventsUs(from using lookaround)
blah
End Function
[/KODE]";

preg_match_all( $expr, $data, $matches, PREG_SET_ORDER );
$parsed = array();

foreach( $matches as $match ) {
if( !empty($match[1]) ) // check if [kode] was found
	continue;
$parsed[] = array(
	'name' => $match[3],
	'arguments' => $match[4],
	'body' => trim($match[5])
);
}

print_r( $parsed );

?>

 

Output

Array
(
    [0] => Array
        (
            [name] => Test
            [arguments] => 
            [body] => 'some code
        )

    [1] => Array
        (
            [name] => TestFunc
            [arguments] => 
            [body] => 'some code
        )

    [2] => Array
        (
            [name] => TestFunc2
            [arguments] => Rng As Range
            [body] => 'some code
        )

)

 

RegEx explained

 


(\[kode\])?\s*(sub|function)\s+([a-z0-9]+)\(([a-z0-9 ]*)\)(.*?)end \2

Options: dot matches newline; case insensitive

Match the regular expression below and capture its match into backreference number 1 «(\[kode\])?»
   Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
   Match the character “[” literally «\[»
   Match the characters “kode” literally «kode»
   Match the character “]” literally «\]»
Match a single character that is a “whitespace character” (spaces, tabs, and line breaks) «\s*»
   Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
Match the regular expression below and capture its match into backreference number 2 «(sub|function)»
   Match either the regular expression below (attempting the next alternative only if this one fails) «sub»
      Match the characters “sub” literally «sub»
   Or match regular expression number 2 below (the entire group fails if this one fails to match) «function»
      Match the characters “function” literally «function»
Match a single character that is a “whitespace character” (spaces, tabs, and line breaks) «\s+»
   Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+»
Match the regular expression below and capture its match into backreference number 3 «([a-z0-9]+)»
   Match a single character present in the list below «[a-z0-9]+»
      Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+»
      A character in the range between “a” and “z” «a-z»
      A character in the range between “0” and “9” «0-9»
Match the character “(” literally «\(»
Match the regular expression below and capture its match into backreference number 4 «([a-z0-9 ]*)»
   Match a single character present in the list below «[a-z0-9 ]*»
      Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
      A character in the range between “a” and “z” «a-z»
      A character in the range between “0” and “9” «0-9»
      The character “ ” « »
Match the character “)” literally «\)»
Match the regular expression below and capture its match into backreference number 5 «(.*?)»
   Match any single character «.*?»
      Between zero and unlimited times, as few times as possible, expanding as needed (lazy) «*?»
Match the characters “end ” literally «end »
Match the same text as most recently matched by capturing group number 2 «\2»

Link to comment
Share on other sites

Thanks for replying again, i used the code

$expr = '/(\[KODE\])?\s*(sub|function)\s+([a-z0-9]+)\(([a-z0-9 ]*)\)(.*?)end \2/si';

preg_match_all( $expr, $post['message'], $matches, PREG_SET_ORDER );
$parsed = array();

foreach( $matches as $match ) {
if( !empty($match[1]) ) // check if [kode] was found
continue;
$parsed[] = array(
'name' => $match[3],
'arguments' => $match[4],
'body' => trim($match[5])
);
}

//print_r( $parsed );

I replaced $data with $post['message'] because thats the source of the data, i had to comment out the //print_r($parsed); as it was giving an error "headers already sent" but thats just down to the way vbulletin works, even with that fault top left of the screen it displayed "Array()" and no replacements were made, even after commenting out the the print command and running it no replacemnets were made.

 

Am i using it wrong? did i need something from the Output code?

Link to comment
Share on other sites

This wasn't meant to be a plug-in solution, more of a guideline to help you accomplish what you're trying to do.

 

If you need help integrating the RegEx into 3rd-party code, you could try the 3rd Party PHP Scripts forum.

 

My example was more for educational purposes.

Link to comment
Share on other sites

Lol, i understand you we're trying to teach me (and im no young pup!) and vbulletin has a plugin area designed specifically for php where you just add the php code without the start and end <?.

 

I dont understand REGEX or the scenarios of where we'd use look forward, look back, look around...etc, so i have to admit i was looking for a solution which i could then later try and decipher as to why it works and what it's doing.

 

Still, ido appreciate everything you've done so far, thanks :)

 

Regards,

Simon

Link to comment
Share on other sites

There's no 'lookaround' in my expression. I took it out because you can't have variable-length lookarounds.

 

I prefer to check if [kode] is there using a capturing group and ?, and then allow PHP's logic to handle if the data should be kept or discarded. Doing this, I can check for [kode] followed by 0 or more white-space characters, followed by sub/function. If [kode] is found, the logic later discards the results of that match.

 

Is there any part of my solution that needs clarification?

Link to comment
Share on other sites

It didnt make any replacements at all, why would that be do you think? in the previous preg_replace it worked too well and doubled sometimes tripled up so im wondering why your latest solution didnt replace anything? even if i tested with only one section of data to look at

Sub Test() 
    If This = "" Then 
        Msgbox "This is that" 
    End If 
End Sub

it didnt replace anything in that string, did i need the print command uncommented? i thought that may have just been to show the results on screen?

 

Regards,

Simon

 

P.S When i said i'm no young pup i meant you cant teach an old dog new tricks :) well not quickly anyway!

Link to comment
Share on other sites

You're testing this code in an environment I can't easily replicate, so it's very hard to tell...

 

I don't know what kind of values or returns vBulletin expects.

 

This might help you further (uses the snippet above, with this change to the foreach)

foreach( $matches as $match ) {
if( !empty($match[1]) ) // check if [kode] was found
	continue;
echo "[KODE]$match[2] $match[3]($match[4]) $match[5] End $match[2][/KODE]\n";
}

 

Outputs

 

[KODE]Sub Test() 
'some code
End Sub[/KODE]
[KODE]Function TestFunc() 
'some code
End Function[/KODE]
[KODE]Function TestFunc2(Rng As Range) 
'some code
End Function[/KODE]

Link to comment
Share on other sites

Ok changed the entire for each like this

//foreach( $matches as $match ) {
//if( !empty($match[1]) ) // check if [kode] was found
//continue;
//$parsed[] = array(
//'name' => $match[3],
//'arguments' => $match[4],
//'body' => trim($match[5])
//);
//}
foreach( $matches as $match )
{
if( !empty($match[1]) ) // check if [kode] was found
continue;	
echo "[KODE]$match[2] $match[3]($match[4]) $match[5] End $match[2][/KODE]\n";
}

There wasn't a replacement if no tags existed and no echo and there wasn't the echo if tags existed, i even replaced echo with print but nothing appeared?

Link to comment
Share on other sites

Would it be easier to go back to

pattern1 = "/Sub\s/";
$pattern2 = "/End Sub/";
$pattern3 = "/Function\s/";
$pattern4 = "/End Function/"; 
$replace1 = "[KODE]Sub ";
$replace2 = "End Sub[/KODE]";
$replace3 = "[KODE]Function ";
$replace4 = "End Function[/KODE]";
$MyString= preg_replace($pattern1, $replace1, $post['message']);
$MyString= preg_replace($pattern2, $replace2, $post['message']);
$MyString= preg_replace($pattern3, $replace3, $post['message']);
$MyString= preg_replace($pattern4, $replace4, $post['message']);
$MyString= preg_replace('/\\[code\\](.*)\\[\/code\\]/siU', '[KODE]\\1[/KODE]', $post['message']); 

and maybe add

/(?<![KODE])Sub/ will only match "Sub" if it is not preceded by "[KODE]".

I found that on the net but i don't know how to implement it for each of the expressions and i especially have no idea how to implement it for following "End Sub"...etc

 

Regards,

Simon

Link to comment
Share on other sites

Sorry $MyString should read differently

$pattern1 = "/(?<![KODE])Sub\s/";
$pattern2 = "/(?![/KODE])End Sub/";
$pattern3 = "/Function\s/";
$pattern4 = "/End Function/"; 
$replace1 = "[KODE]Sub ";
$replace2 = "End Sub[/KODE]";
$replace3 = "[KODE]Function ";
$replace4 = "End Function[/KODE]";
$post['message']= preg_replace($pattern1, $replace1, $post['message']);
$post['message']= preg_replace($pattern2, $replace2, $post['message']);
$post['message']= preg_replace($pattern3, $replace3, $post['message']);
$post['message']= preg_replace($pattern4, $replace4, $post['message']);
$post['message']= preg_replace('/\\[code\\](.*)\\[\/code\\]/siU', '[KODE]\\1[/KODE]', $post['message']);

I tried adding the negative assertion to check the beginnig and end, i got an error back "..Unknown qualifier "K"...", so i tried to escape it like this \\[KODE\\]..etc but still an error, im just shooting in the dark as i don't have the foggiest what im doing.

 

Regards,

Simon

Link to comment
Share on other sites

I really would love to help you further, but I just don't know how the vBulletin BBParser works.

 

It seems like matching the opening and closing tags separately is a bad idea in this case, because extra processing is needed to make sure there are as many opening tags and closing. If we were wanting to check for nested values, though, we'd definitely want to check separately.

 

Perhaps if you elaborated how the system worked, or provided me with a link or tutorial on how I should format my help. I'm still not going to give you the copy+paste solution, but I might be able to guide you, rather than throwing code at you blindly ;)

Link to comment
Share on other sites

Hi would it help to have test forum access (although you'd have to replace [ code ] tags there) or would it help to have the bbcode.php?, to be honest with you all that needs checking is, is there already a [KODE] tag there before SUB or FUNCTION if yes skip END SUB or END FUNCTION and look for next SUB or FUNCTION do the check again if no [KODE] tag is present then add [KODE]just before it, find the next END SUB or END FUNCTION and after it add [/KODE].

 

Like i said above the one where i do each seperately works but too well as it doubles or tripples up depending on how many instances of code there are in the post being made, thats why i tried adding the negative assertion code to one of them to try it but couldn't get the syntax correct.

 

I appreciate this is probably frustrating to you and really appreciate you sticking with it.

 

Regards,

Simon

Link to comment
Share on other sites

Ok, this works perfect but only if i remove the:

Function MyTest()

blah

End Function

 

The word Function makes it nuts!, any ideas?

$post['message'] = preg_replace('/\\[code\\](.*)(?<!\[\/KODE\])\\[\/code\\]/siU', '[KODE]\\1[/KODE]', $post['message']); 

$post['message'] = preg_replace("/(?<!\[KODE\]|End\s)Sub(?<!\s\[\/KODE\])(.*)End Sub/siU", '[KODE]Sub \\1 End Sub[/KODE]', $post['message']);

$post['message'] = preg_replace("/(?<!\[KODE\]|End\s)Function(?<!\s\[\/KODE\])(.*)End Function/siU", '[KODE]Function \\1 End Function[/KODE]', $post['message']);

So how can we change the last preg_replace to ignore the word FUNCTION as a command word?

 

Regards,

Simon

Link to comment
Share on other sites

<?php

$expr = '/(\[kode\])?\s*(sub|function)\s+([a-z0-9]+)\(([a-z0-9 ]*)\)(.*?)end \2/si';
$data = "Sub Test()
'some code
End Sub
[KODE]Sub Test2(MyTest by Val)
'some code
End Sub[/KODE]
Function TestFunc()
'some code
End Function
Function TestFunc2(Rng As Range)
'some code
End Function
[KODE]
Function preventsUs(from using lookaround)
blah
End Function
[/KODE]";

$data = preg_replace_callback($expr, 'replace_function', $data);

echo $data;

function replace_function( $match ) {
if( empty($match[1]) ) // check if [kode] was found
	return "[KODE]$match[0][/KODE]";
else
	return $match[0];
}

?>

 

If you use >= PHP5.3, you can use an anonymous function instead.

Link to comment
Share on other sites

I can't really help you unless you show me how you implemented it, and what the error message was. Try

 

<?php

$data = preg_replace_callback($expr,
create_function('$match',
	'if( empty($match[1]) )
		return "[KODE]$match[0][/KODE]";
	else
		return $match[0];'),
$data);

?>

Link to comment
Share on other sites

I implemented it by replacing all the code for that supplied, running it last time and with the revised code gave this error

Warning preg_replace_callback() [function preg-replace-callback] Delimieter must not be alphanumeric or backslash in [path]/includes/functions_newpost.php(414):eval()'d code on line 7
i know you were expecting an error and for it to give you some clue but other than a delimiter issue i cant help you understand my issue :(
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.