trzczy
trzczy

Reputation: 1491

preg_match_all 'OR' operator

I cannot get 'today' value from the string. The pattern is to get strings placed inside double curly braces and strings placed outside double curly braces.

I am getting only 'inside' values. The pattern is:

/({{(?P<inside>[^}{]+)}}|}}(?P<outside>[^}{]+){{)/

The string is

{{Friday}}today{{Sunday}}

The php code is:

$returnValue = preg_match_all('/({{(?P<inside>[^}{]+)}}|}}(?P<outside>[^}{]+){{)/', '{{Friday}}today{{Sunday}}', $matches);

This returns only that:

    array (
  0 => 
  array (
    0 => '{{Friday}}',
    1 => '{{Sunday}}',
  ),
  1 => 
  array (
    0 => '{{Friday}}',
    1 => '{{Sunday}}',
  ),
  'inside' => 
  array (
    0 => 'Friday',
    1 => 'Sunday',
  ),
  2 => 
  array (
    0 => 'Friday',
    1 => 'Sunday',
  ),
  'outside' => 
  array (
    0 => '',
    1 => '',
  ),
  3 => 
  array (
    0 => '',
    1 => '',
  ),
)

There is nothing about 'today' in the array above.

What is interesting when the parts of the pattern was replaced each other there were no 'Sunday' nor 'Suterday' values in the result but 'today'.

It looks like consider only the first part of pattern.

What pattern to get all values? Thank you.

Edited:

1. Thanks the really great answers. But I must extend the problem a little. I testet your all propositions and they work good for the string

    {{Friday}}today{{Sunday}}

But they do not work for such a strings:

    {{Friday}}t{od{a}y{{Sunday}}

It should be such a value in the result

t{od{a}y

Cause only the double curly braces should be delimeters.

I tried to adapt your great patterns to that requirements but not succeed. The substring '}}' and '{{' should be negated. Not only single '{' or '}'.

How to get it?

2. Another problem but not so important is that the 'inner' and 'outer' result arrays should not have empty strings. See on the picture what i mean: enter image description here

Upvotes: 3

Views: 2261

Answers (3)

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 627101

Here is my solution:

$re = "/{{(?<inner>.*?)}}|(?<outer>[^{}]*(?:{(?!{)[^{}]*|}(?!})[^{}]*)*)/"; 
$str = "{{Friday}}today{{Sunday}}"; 
preg_match_all($re, $str, $matches);
print_r(array_filter($matches["outer"]));
print_r(array_filter($matches["inner"]));

Why does it look so complex?

The regex contains 2 alternatives, one is for capturing all substrings inside {{ and }} into Group "inner", and the Group "outer" captures all substrings other than those inside {{ and }}. Let me explain these patterns:

Part 1:
{{(?<inner>.*?)}}
This finds and consumes {{ substring, followed with zero or more characters other than a newline (use an /s dotall modifier at the end of the regex to also match newlines), zero or more occurrences (use .+? to match one or more), as few as possible (meaning we stop at the first }}). Then, }} are matched.

Part 2:
(?<outer>[^{}]*(?:{(?!{)[^{}]*|}(?!})[^{}]*)*)
This subpattern is equal to (?<outer>(?:(?!{{(?!{)|}}(?!})).)*), but is an unrolled version of this tempered greedy token (and is thus more efficient than a tempered greedy token with 2 alternatives). It just matches non-braces [^{}]* (zero or more) followed with zero or more sequences (=optionally matches) of a { not followed with another { or } not followed with another } and then zero or more non-braces.

The array_filter removes empty elements from the resulting arrays.

And here is a regex demo

Upvotes: 1

trzczy
trzczy

Reputation: 1491

I have managed it. The problem of empty strings still remains. The single braces do not make a problem.

(?<={{)(?P<THINGS>((?<!}}).)+)(?=}})|((?<=}})(?P<AREA>((?<!{{).)+)(?={{))|(?P<AREA2>^((?<!{{).)+)(?={{)|(?<=}})(?P<AREA3>((.)+$))

Debuggex Demo

The result array:

array (
  0 => 
  array (
    0 => 'Fo{rest',
    1 => 'Go{l}d',
    2 => 'Beach',
    3 => 'Monay',
    4 => 'Grass',
    5 => 'Diamond',
    6 => 'S{ea',
  ),
  'THINGS' => 
  array (
    0 => '',
    1 => 'Go{l}d',
    2 => '',
    3 => 'Monay',
    4 => '',
    5 => 'Diamond',
    6 => '',
  ),
  1 => 
  array (
    0 => '',
    1 => 'Go{l}d',
    2 => '',
    3 => 'Monay',
    4 => '',
    5 => 'Diamond',
    6 => '',
  ),
  2 => 
  array (
    0 => '',
    1 => 'd',
    2 => '',
    3 => 'y',
    4 => '',
    5 => 'd',
    6 => '',
  ),
  3 => 
  array (
    0 => '',
    1 => '',
    2 => 'Beach',
    3 => '',
    4 => 'Grass',
    5 => '',
    6 => '',
  ),
  'AREA' => 
  array (
    0 => '',
    1 => '',
    2 => 'Beach',
    3 => '',
    4 => 'Grass',
    5 => '',
    6 => '',
  ),
  4 => 
  array (
    0 => '',
    1 => '',
    2 => 'Beach',
    3 => '',
    4 => 'Grass',
    5 => '',
    6 => '',
  ),
  5 => 
  array (
    0 => '',
    1 => '',
    2 => 'h',
    3 => '',
    4 => 's',
    5 => '',
    6 => '',
  ),
  'AREA2' => 
  array (
    0 => 'Fo{rest',
    1 => '',
    2 => '',
    3 => '',
    4 => '',
    5 => '',
    6 => '',
  ),
  6 => 
  array (
    0 => 'Fo{rest',
    1 => '',
    2 => '',
    3 => '',
    4 => '',
    5 => '',
    6 => '',
  ),
  7 => 
  array (
    0 => 't',
    1 => '',
    2 => '',
    3 => '',
    4 => '',
    5 => '',
    6 => '',
  ),
  'AREA3' => 
  array (
    0 => '',
    1 => '',
    2 => '',
    3 => '',
    4 => '',
    5 => '',
    6 => 'S{ea',
  ),
  8 => 
  array (
    0 => '',
    1 => '',
    2 => '',
    3 => '',
    4 => '',
    5 => '',
    6 => 'S{ea',
  ),
  9 => 
  array (
    0 => '',
    1 => '',
    2 => '',
    3 => '',
    4 => '',
    5 => '',
    6 => 'S{ea',
  ),
  10 => 
  array (
    0 => '',
    1 => '',
    2 => '',
    3 => '',
    4 => '',
    5 => '',
    6 => 'a',
  ),
)

The php code:

$returnValue = preg_match_all('/(?<={{)(?P<THINGS>((?<!}}).)+)(?=}})|((?<=}})(?P<AREA>((?<!{{).)+)(?={{))|(?P<AREA2>^((?<!{{).)+)(?={{)|(?<=}})(?P<AREA3>((.)+$))/', 'Fo{rest{{Go{l}d}}Beach{{Monay}}Grass{{Diamond}}S{ea', $matches);

Upvotes: 1

zolo
zolo

Reputation: 469

(?<=\{\{)(?<inside>[^\}\{ $]+)|(?<=\}\})(?<outside>[^\{\} $]+)

DEMO

Please let me know if this works to you.

Upvotes: 0

Related Questions