【问题标题】:How to get preg_match to not cross the results?如何让 preg_match 不超过结果?
【发布时间】:2011-08-05 08:34:16
【问题描述】:

我有一个问题。我在[%%] 之间寻找一些东西。我可以毫无问题地捕捉到[%some var%](它捕捉到了"some var")。

但是,当我有这个时:

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

然后它抓住了

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

我该如何解决这个问题?在我看来,我可以先检查[% %]%] 匹配,但它不是解决办法

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

如果我可以设法重写正则表达式以忽略 [%%] 但两者之间没有另一个 [%%] 。反正我对正则表达式的了解很差,但是我决定用正则表达式代替strpos的...


EDIT0

好吧,我更喜欢使用 while 循环来替换内部没有其他 [% 或 %] 的 [% %]...

我的意思是:

例如: 我定义了某些替换:

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

替换函数已经创建(如果没有递归,则可以正常工作)。这样做

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

结果:

prefix<replacement variable's value>suffix

这已经可以了。

示例文本:

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

因此循环应该使用示例文本:

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

希望你现在明白这一点。


编辑1

可能我只是不需要正则表达式,因为这太复杂了。也许我只需要编写自己的解析器。在它命中 %] 之后,它基本上会检查是否没有双,未关闭的 [% 之前。是的...这应该可以解决问题,但请仍然尝试帮助我。 :) 谢谢!


编辑2

终于有办法了!

现在真的可以了

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 的建议在稍作修改后 100% 适用。谢谢,山姆·格雷厄姆!

【问题讨论】:

  • 递归不能用正则表达式解决。改为使用具有递归下降的解析器
  • @knittl,Perl、PHP 和 .NET 的正则表达式实现可以处理递归模式。
  • 从技术上讲,这不再是正则表达式了
  • @knittl,你的意思可能是“理论上”:)。但是我没有使用“正则表达式”这个词,而是“正则表达式实现”。 OP 要求的是技术解决方案,而不是语言理论课。请注意,这是 Stackoverflow,其中技术问题 w.r.t.询问的是编程语言,而不是(理论上的)计算机科学 SE 站点。
  • @lmojzis,我在您的问题中粘贴了来自pastebin 的编辑。

标签: php regex parsing


【解决方案1】:

我会使用(\[%((?:[^\[]|\[(?!%))*?)%\])

分解:

(        // 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

或者用英语说,匹配一个 [% 然后匹配任何不是另一个 [% 直到找到第一个 %],记住 [% %] 中间的位和包括 [%] 在内的整个内容%]。

您可以使用以下 php 脚本进行测试:

<?         
$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";
    }
}
?>

应该给你输出:

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!

【讨论】:

  • 看起来很有希望。我会检查并尝试修改它以符合我的情况并尽快报告结果:) 无论如何 THX :)
【解决方案2】:

如果您准备为每个嵌套级别执行一个正则表达式循环(如您的编辑所建议的那样),那么这很容易:

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

对每一层嵌套执行一次。

【讨论】:

    【解决方案3】:

    要匹配您可以使用的嵌套标签:

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

    【讨论】:

      猜你喜欢
      • 2011-05-18
      • 2021-10-27
      • 2022-08-20
      • 1970-01-01
      • 2013-01-24
      • 1970-01-01
      • 2013-02-23
      • 1970-01-01
      • 2015-10-24
      相关资源
      最近更新 更多