开发者

How to get preg_match to not cross the results?

I've got a problem. I am raplacing something in between [% and %]. I can catch [%some var%] with no problems (it catches "some var").

However, when I have this:

[%something|[%with parameter that should be parsed before/after as well%]%]

then it catches

"something|[%with parameter that should be parsed before/after as well"

How can I fix this? In my opinion I could check for [% %]%] match first, but it is no sollution when it's like

[%something|[%with parameter that should be parsed before/after as well%] and something unparsed%]

Maybe if I could manage to rewrite the regexp to ignore when there's [% and %] but isn't another [% or %] in between. Anyway, my knowledge of regexps is poor, but I decided to use regexps instead of strpos's...


EDIT0

Well, I 'd prefer to use while cycle to replace [% %]'s that has no other [% or %] inside on the fly...

I mean:

for example: I have certain replacements defined:

post.date = 1. 1. 1970
post.time = 00:00:00
post.creator.name = John Smith
post.creator.age = (computed) 64

replacement function is already created (and works properly if there's no recursion). Does this

[%<replacement variable name>|(optional) prefix|(optional) suffix%]

result:

prefix<replacement variable's value>suffix

this already works.

Sample text:

"This post was created on [%post.date%][%post.time%| at ][%post.creator.name| by [%post.creator.age||years old %]user%]."

Therefore the cycle should to this with the sample text:

Step 0: "This post was created on 1. 1. 1970[%post.time%| at ][%post.creator.name| by [%post.creator.age||years old %]user%]."
Step 1: "This post was created on 1. 1. 1970 at 00:00:00[%post.creator.name| by [%post.creator.age|| years old %]user%]."
Step 2: "This post was created on 1. 1. 1970 at 00:00:00[%post.creator.name| by 64 years old user%]."
Step 3: "This post was created on 1. 1. 1970 at 00:00:00 by 64 years old user John Smith."

Hope you now see the point.


EDIT1

Probably I just don't need regexps as this is way too much complex. Maybe I just need to write my own parser. After it hits %] it would basically check whether there is no double, unclosed [% before. Yep... That should do the trick but PLEASE, still try t开发者_如何学运维o help me though. :) Thanks!


EDIT2

Finally got a sollution!

Now it really does

This is a post[%post.date| on %][%post.time| at %][%post.creator.name| by [%post.creator.age|| years old %]user %].
Step 0: This is a post on 1. 1. 1970[%post.time| at %][%post.creator.name| by [%post.creator.age|| years old %]user %].
Step 1: This is a post on 1. 1. 1970 at 00:00:00[%post.creator.name| by [%post.creator.age|| years old %]user %].
Step 2: This is a post on 1. 1. 1970 at 00:00:00[%post.creator.name| by 64 years old user %].
Step 3: This is a post on 1. 1. 1970 at 00:00:00 by 64 years old user John Smith.

Sam Graham's advice is 100% applicable with minor editing. Thanks, Sam Graham!


I'd use (\[%((?:[^\[]|\[(?!%))*?)%\]).

To break it down:

(        // Start capturing group 1 for the entire [%...%] block
\[%      // Match a literal [%
(        // Start capturing group 2 for the inner contents of the [%...%] block
(?:      // Start a non-capturing group of alternative matches
[^\[]    // Match anything that isn't a literal [
|        // or
\[(?!%)  // Match a literal [ that isn't followed by a %
)        // End list of alternative matches
*?       // Match as few as possible of the previous item
)        // End capture group 2
%\]      // Match a literal %]
)        // End capture group 1

Or to put it in english, match a [% then anything that isn't another [% until you find the first %], remembering the bit in the middle of the [% %] and the entire thing including [% %].

You can test with the following php script:

<?         
$tests = array(
    "[%something|[%with parameter that should be parsed before/after as well%]%]",
    "[%something%][%something else%]",
    );

foreach ($tests as $test) {
    echo "Testing $test:\n";
    $loop = 0;
    while (preg_match("/(\[%((?:[^\[]|\[(?!%))*?)%\])/", $test, $matches)) {
        $loop++; 
        echo "  First loop, looking at $test:\n";
        echo "    group 1: $matches[1]\n    group 2: $matches[2]\n";
        //  Do whatever here...
        $test = str_replace($matches[1], "REPLACED!", $test);
        echo "    replaced: $test\n";
    }
}
?>

Should give you output:

Testing [%something|[%with parameter that should be parsed before/after as well%]%]:
  First loop, looking at [%something|[%with parameter that should be parsed before/after as well%]%]:
    group 1: [%with parameter that should be parsed before/after as well%]
    group 2: with parameter that should be parsed before/after as well
    replaced: [%something|REPLACED!%]
  First loop, looking at [%something|REPLACED!%]:
    group 1: [%something|REPLACED!%]
    group 2: something|REPLACED!
    replaced: REPLACED!
Testing [%something%][%something else%]:
  First loop, looking at [%something%][%something else%]:
    group 1: [%something%]
    group 2: something
    replaced: REPLACED![%something else%]
  First loop, looking at REPLACED![%something else%]:
    group 1: [%something else%]
    group 2: something else
    replaced: REPLACED!REPLACED!


If you're prepared to do one cycle of regex for each level of nesting (as suggested by your edits) then it's easy:

$result = preg_replace_callback(
    '/\[%     # Match [%
    (         # Match and capture...
     (?:      # the following:
      (?!     # If the next part of the string is neither...
       \[%    #  [%
      |       # nor
       %\]    #  %]
      )       # (End of lookahead)
      .       # then match any character.
     )*       # Do this any number of times.
    )         # End of capturing group.
    %\]       # Match %]
    /x', 
    'compute_replacement', $subject);

function compute_replacement($groups) {
    // $groups[1] holds the text between [%...%]
    return 'myreplacement';
}

Do this once for every level of nesting.


To match nested tags like those you could use:

\[%((?:[^[%]++|\[(?!%)|%(?!])|(?R))*)%]
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜