简体   繁体   中英

Regex: How to not include a regex if is in another regex

Here is my problem: I have a php file which contain this "html" code:

<div>
    {{ '{# Hi :D #}' }} {# Hello #}
    {{ $model }}
</div>

In my code I want to get the {# #} and the {{ }} to do different regex replacement but not the one that are in {{ }}.

The problem here is that if I do in the same (in PHP) preg_replace for the two possible match, this doesn't take the {{ }} found (That's what I want) but the replacement will be the same for the two:

/{{(.*?)}}|{#(.*?)#}/

This is logic but I want to do different replacements for each $1 and $2 match.

I thought to those possibilities :

  • First is to tell the regex to do not take the strings that are in the "{{ }}" parent but after multiple tentatives I failed to.
  • Second possibility is to add another replacement, in the preg_replace function you can do an array() replacement but this doesn't work like $1 = the first replacement and $2 = the second replacement, there is a replacement per regex request.

After 2 days trying to create this magic regex I finally ask a question in stackoverflow, hope someone will found the answer to this regex

Thanks,

EDITED

Here's an approach using preg_split() with PREG_SPLIT_DELIM_CAPTURE flag in order to tokenize the string and account for nested tags .

$tokens = [
  // token definition as [open_tag, close_tag, replacement]
  ['{{', '}}', '<?php echo \1; ?>'],
  ['{#', '#}', '<?php //\1 ?>']
];

$open_tags = array_column($tokens, 0);
$close_tags = array_column($tokens, 1, 0); // mapped by open tag
$replacements = array_column($tokens, 2, 0); // mapped by open tag

$token_regex = '/(' . implode('|', array_map('preg_quote', $open_tags + $close_tags)) . ')/';

$parts = preg_split($token_regex, $input, -1, PREG_SPLIT_DELIM_CAPTURE);

$output = '';
while (null !== ($part = array_shift($parts))) {

  // open tag found...
  if (in_array($part, $open_tags)) {
    // ...start building string of full tag
    $tag_body = $part;
    // ...watch for corresponding close tag
    $close_at = [ $close_tags[$part] ];
    while (0 < count($close_at)) {
      $inner_part = array_shift($parts);
      if (in_array($inner_part, $open_tags)) {
        // nested tag found, add its closing to watchlist
        array_unshift($close_at, $close_tags[$inner_part]);
      } else {
        // close tag found, remove from watchlist
        if (reset($close_at) === $inner_part) {
          array_shift($close_at);
        }
      }
      $tag_body .= $inner_part;
    }

    // substitute full tag with replacement
    $tag_regex = '/^' . preg_quote($part) . '\s*(.*?)\s*' . preg_quote($close_tags[$part]) . '$/';
    $part = preg_replace($tag_regex, $replacements[$part], $tag_body);
  }

  $output .= $part;
}

echo $output;

You can try it out here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM