Match curly brace wrapped characters in a slash delimited string - php

I'm trying to match a substring which contains no curly braces or forward slashes AND is wrapped by curly braces THEN wrapped by delimiting forward slashes.
Pseudocode: /{ any string not contain "/" and "{" and "}" inside }/
My test string /a/{bb}/{d{b}}/{as}df}/b{cb}/a{sdfas/dsgf}
My failed pattern: \/\{((?!\/).)*\}\/
My failed result:
array(2)
=> array(2)
=> /{bb}/
=> /{as}df}/
)
=> array(2)
=> b
=> f
)
)
I want it to only match /{bb}/ and isolate bb.

You can try this mate
(?<=\/){[^\/{}]*?}(?=\/)
Explanation
(?<=\/) - Positive look behind. Matches /
{ - Matches {.
[^\/{}]*? - Matches everything except { and } and / zero or more time ( lazy mode ).
(?=\/) - Matches /.
You can use this too \/({[^\/{}]*?})\/
Demo

I extremely suggest you to use https://regex101.com/ website to test and debug your regex
this regex will work for you: (?<=/){([^/{}]+?)}(?=/)

To ensure that the whole substring between delimiting slashes is a solitary value wrapped in curly braces, I recommend that you check that:
the match starts with a delimiting slash or is at the start of the string and
the curly-brace-wrapped value does not contain any delimiting slashes or curly braces and
the match is immediately followed by a delimiting slash or is at the end of the string.
Lazy matching is not necessary/beneficial in the pattern because the negated character class will prevent the possibility of "over matching".
Cod: (Demo)
$string = '/a/{bb}/{d{b}}/{as}df}/b{cb}/a{sdfas/dsgf}';
var_export(
preg_match(
'~(?:^|/){([^{}/]*)}(?:/|$)~',
$string,
$out
)
? $out
: 'no match'
);
Output:
array (
0 => '/{bb}/', // the fullstring match
1 => 'bb', // capture group 1
)

Related

Regex for find value between curly braces which have pipe separator

$str = ({max_w} * {max_h} * {key|value}) / {key_1|value}
I have the above formula, I want to match the value with curly braces and which has a pipe separator. Right now the issue is it's giving me the values which have not pipe separator. I am new in regex so not have much idea about that. I tried below one
preg_match_all("^\{(|.*?|)\}^",$str, PREG_PATTERN_ORDER);
It gives below output
Array
(
[0] => key|value
[1] => max_w
[2] => max_h
[3] => key_1|value
)
Expected output
Array
(
[0] => key|value
[1] => key_1|value
)
Not sure about PHP. Here's the general regex that will do this.
{([^{}]*\|[^{}]*)}
Here is the demo.
You can use
(?<={)[^}]*\|[^}]*(?=})
For the given string the two matches are shown by the pointy characters:
({max_w} * {max_h} * {key|value}) / {key_1|value}
^^^^^^^^^ ^^^^^^^^^^^
Demo
(?<={) is a positive lookbehind. Arguably, the positive lookahead (?=}) is not be needed if it is known that all braces appear in matching, non-overlapping pairs.
The pattern \{(|.*?|)\} has 2 alternations | that can be omitted as the alternatives on the left and right of it are not really useful.
That leaves \{(.*?)} where the . can match any char including a pipe char, and therefore does not make sure that it is matched in between.
You can use a pattern that does not crosses matching a curly or a pipe char to match a single pipe in between.
{\K[^{}|]*\|[^{}|]*(?=})
{ Match opening {
\K Forget what is matches until now
[^{}|]* Match any char except the listed
\| Match a | char
[^{}|]* Match any char except the listed
(?=}) Assert a closing } to the right
Regex demo | PHP demo
$str = "({max_w} * {max_h} * {key|value}) / {key_1|value}";
$pattern = "/{\K[^{}|]*\|[^{}|]*(?=})/";
preg_match_all($pattern, $str, $matches);
print_r($matches[0]);
Output
Array
(
[0] => key|value
[1] => key_1|value
)
Or using a capture group:
{([^{}|]*\|[^{}|]*)}
Regex demo

How to get the text between any number of parenthesis?

Suppose I have a document where I want to capture the strings that have parenthesis before or after.
Example: This [is] a {{test}} sentence. The (((end))).
So basically I want to get the words is, test and end.
Thanks in advance.
According to your condition "strings that have parenthesis before or after" - any word could be proceeded with OR only followed by some type of parentheses:
$text = 'This [is] a {{test}} sentence. The (((end))). Some word))';
preg_match_all('/(?:\[+|\{+|\(+)(\w+)|(\w+)(?:\]+|\}+|\)+)/', $text, $m);
$result = array_filter(array_merge($m[1],$m[2]));
print_r($result);
The output:
Array
(
[0] => is
[1] => test
[2] => end
[7] => word
)
The below code works for me.
<?php
$in = "This [is] a {{test}} sentence. The (((end))).";
preg_match_all('/(?<=\(|\[|{)[^()\[\]{}]+/', $in, $out);
echo $out[0][0]."<br>".$out[0][1]."<br>".$out[0][2];
?>
Your regex could be:
[\[{(]((?(?<=\[)[^\[\]]+|(?(?<={)[^{}]+|[^()]+)))
Explanation: the if-then-else construction is needed to make sure that an opening '{' is matched against a closing '}', etc.
[\[{(] # Read [, { or (
((?(?<=\[) # Lookbehind: IF preceding char is [
[^\[\]]+ # THEN read all chars unequal to [ and ]
| # ELSE
(?(?<={) # IF preceding char is {
[^{}]+ # THEN read all chars unequal to { and }
| # ELSE
[^()]+))) # read all chars unequal to ( and )
See regex101.com
Try this Regex:
(?<=\(|\[|{)[^()\[\]{}]+
>>>Demo<<<
OR this one:
(?<=\(|{|\[)(?!\(|{|\[)[^)\]}]+
>>>Demo<<<
Explantion(for the 1st regex):
(?<=\(|\[|{) - Positive lookbehind - looks for a zero-length match just preceeded by a { or [ or a (
[^()\[\]{}]+ - one or more occurences of any character which is not amoong the following characters: [, (, {, }, ), ]
Explanation(for 2nd Regex):
(?<=\(|\[|{) - Positive lookbehind - looks for a zero-length match just preceeded by a { or [ or a (
(?!\(|{|\[) - Negative lookahead - In the previous step, it found the position which is just preceded by an opening bracket. This piece of regex verifies that it is not followed by another opening bracket. Hence, matching the position just after the innermost opening bracket - (, { or [.
[^)\]}]+ - One or more occurrences of characters which are not among these closing brackets - ], }, )

Get all occurrences between closest double brackets

Given string $str = 'aa {{asd}} bla {{{888 999}} {555} 777 uiii {{-i {{qw{er}}';
Need get all occurrences between closest opening-closing double curly brackets.
Desirable result:
asd
888 999
qw{er
If try: preg_match_all('#\{\{(.*?)\}\}#', $str, $matches);
Current output:
asd
{888 999
-i {{qw{er
though, these occurrences aren't between closest double curly brackets.
Question is: what is appropriate pattern for this?
You can use this pattern:
\{\{(?!\{)((?:(?!\{\{).)*?)\}\}
The trick here is to use a negative lookahead like (?!\{\{) to avoid matching nested brackets.
\{\{ # match {{
(?!\{) # assert the next character isn't another {
(
(?: # as few times as necessary...
(?!\{\{). # match the next character as long as there is no {{
)*?
)
\}\} # match }}
Regex demo
Regex: (?<=\{{2})(?!\{)[\s\w\{]+(?=\}\})
(?=\}\}) Should contain double curly braces ahead
(?<=\{{2}) Should contain curly braces behind
(?!\{) should not contain curly braces one curly brace behind two matched
PHP code:
$str = 'aa {{asd}} bla {{{888 999}} {555} 777 uiii {{-i {{qw{er}}';
preg_match_all("/(?<=\{{2})(?!\{)[\s\w\{]+(?=\}\})/",$str,$matches);
print_r($matches);
Output:
Array
(
[0] => Array
(
[0] => asd
[1] => 888 999
[2] => qw{er
)
)

Grab all characters inside {...} if not contain "{" and "}"

I want catch all character inside { ... },
If inside not found "{" and "}"
So for example:
{amdnia91(\+wowa}
Catch it.
{amdn{ia91(\+wowa}
Not catch (contain "{").
preg_match_all('#(.+?)\{(.+?)\}#', $input, $output);
How fix it?
EDIT.
Explained more:
I will try to create css minifier.
But there i need catch all names and content inside brackets as separate array value.
Curret $input look like this:
.something{property:value;prop2:value}#something2{someprop:val;prop:val}
It is also minfied so containt multiple ...{}...{} inline.
And my code catch all good but...
This catch also if inside brackets its brackets,
but i don't want catch it if contain brackets inside.
[^}{] means match any character that is not } or {.
So:
preg_match_all('#\{([^}{]+)\}#', $input, $output);
However, note that in your {amdn{ia91(+wowa} example, this will match the ia91(+wowa fragment.
EDIT
If you didn't want any match at all for that second example, then try this:
preg_match_all('#^[^}{]*\{([^}{]+)\}[^}{]*$#', $input, $output);
The regex broken down means:
^ - The start of the line
[^}{]* - Any character which is not { or } zero or more times
\{ - The literal { character
([^}{]+) - Capture one or more characters which are not { or }
\} - The literal } character
[^}{]* - Any character which is not { or } zero or more times
$ - The end of the line
Demonstration
Second Edit
Given your further explanation on what you need, I'd suggest this:
preg_match_all('#(?<=^|})[^}{]*?\{([^}{]+?)\}(?=[^}]*$|[^}]*\{)#', $input, $output);
This uses a "look-behind" and a "look-ahead". Broken down, it means:
(?<=^|}) Lookbehind: Assert that this is either the start of the line or that the previous character was a literal '}' but do not include that character as part of the whole match
[^}{]*? - Lazily match zero or more characters which are not { or }
\{ - A literal {
([^}{]+?) - Lazily capture one or more characters which are not { or }
\} - A literal }
(?=[^}]*$|[^}]*\{) - Lookahead: Ensure that the following characters are either zero or more characters which are not } followed by the line end, or zero or more characters which are not } followed by a literal { but do not include those characters as part of the whole match
Demonstration
I am posting an alternative to the regex posted by daiscog based on the concept of matching what we do not need and omitting it, and only match what we need later with the help of PCRE (*SKIP)(*FAIL) verbs:
[#.]?[^{}]*{[^{}]*[{}][^{}]*}(*SKIP)(*F)|[#.]?[^{}]*{([^{}]+)}
See the regex demo
What does it match?
[#.]?[^{}]*{[^{}]*[{}][^{}]*}(*SKIP)(*F) - an optional . or # (see [#.]?) followed with 0+ characters other than { and } (see [^{}]*) followed with a {, that is again followed with [^{}]*, followed with either { or } (see [{}]) and then again [^{}]* and a closing }. This part matches strings like .something{ or nothing. Then, once matched, discard this match from the matches returned due to the (*SKIP)(*FAIL) verbs.
| - or...
[#.]?[^{}]*{([^{}]+)} - an optional . or # (see [#.]?) followed with 0+ characters other than { and } (see [^{}]*), then {, then 1+ characters other than braces ([^{}]+) and a closing brace }. This is what we will keep and get as matches.
PHP demo:
$re = '~[#.]?[^{}]*{[^{}]*[{}][^{}]*}(*SKIP)(*F)|[#.]?[^{}]*{([^{}]+)}~';
$str = "{amdnia91(+wowa}\n{amdn{ia91(+wowa}\n.something{property:value;prop2:value}#something2{someprop:val;prop:val}\n.something{property:value{;prop2:value}#something2{someprop:val;prop:val}\n.something{property:v}alue;prop2:value}#something2{someprop:val;prop:val}";
preg_match_all($re, $str, $matches);
print_r($matches);
Result:
Array
(
[0] => Array
(
[0] => {amdnia91(+wowa}
[1] =>
.something{property:value;prop2:value}
[2] => #something2{someprop:val;prop:val}
[3] => #something2{someprop:val;prop:val}
[4] => #something2{someprop:val;prop:val}
)
[1] => Array
(
[0] => amdnia91(+wowa
[1] => property:value;prop2:value
[2] => someprop:val;prop:val
[3] => someprop:val;prop:val
[4] => someprop:val;prop:val
)
)

PHP: Parse comma-delimited string outside single and double quotes and parentheses

I've found several partial answers to this question, but none that cover all my needs...
I am trying to parse a user generated string as if it were a series of php function arguments to determine the number of arguments:
This string:
$arg1,$arg2='ABC,DEF',$arg3="GHI\",JKL",$arg4=array(1,'2)',"3\"),")
will be inserted as the arguments of a function:
function my_function( [insert string here] ){ ... }
I need to parse the string on the commas, taking into account single- and double-quotes, parentheses, and escaped quotes and parentheses to create an array:
array(4) {
[0] => $arg1
[1] => $arg2='ABC,DEF'
[2] => $arg3="GHI\",JKL"
[3] => $arg4=array(1,'2)',"3\"),")
}
Any help with a regular expression or parser function to accomplish this is appreciated!
It isn't possible to solve this problem with a classical csv tool since there is more than one character able to protect parts of the string.
Using preg_split is possible but will result in a very complicated and inefficient pattern. So the best way is to use preg_match_all. There are however several problems to solve:
as needed, a comma enclosed in quotes or parenthesis must be ignored (seen as a character without special meaning, not as a delimiter)
you need to extract the params, but you need to check if the string has the good format too, otherwise the match results may be totally false!
For the first point, you can define subpatterns to describe each cases: the quoted parts, the parts enclosed between parenthesis, and a more general subpattern able to match a complete param and that uses the two previous subpatterns when needed.
Note that the parenthesis subpattern needs to refer to the general subpattern too, since it can contain anything (and commas too).
The second point can be solved using the \G anchor that ensures that all matchs are contiguous. But you need to be sure that the end of the string has been reached. To do that, you can add an optional empty capture group at the end of the main pattern that is created only if the anchor for the end of the string \z succeeds.
$subject = <<<'EOD'
$arg1,$arg2='ABC,DEF',$arg3="GHI\",JKL",$arg4=array(1,'2)',"3\"),")
EOD;
$pattern = <<<'EOD'
~
# named groups definitions
(?(DEFINE) # this definition group allows to define the subpatterns you want
# without matching anything
(?<quotes>
' [^'\\]*+ (?s:\\.[^'\\]*)*+ ' | " [^"\\]*+ (?s:\\.[^"\\]*)*+ "
)
(?<brackets> \( \g<content> (?: ,+ \g<content> )*+ \) )
(?<content> [^,'"()]*+ # ' # (<-- comment for SO syntax highlighting)
(?:
(?: \g<brackets> | \g<quotes> )
[^,'"()]* # ' #
)*+
)
)
# the main pattern
(?: # two possible beginings
\G(?!\A) , # a comma contiguous to a previous match
| # OR
\A # the start of the string
)
(?<param> \g<content> )
(?: \z (?<check>) )? # create an item "check" when the end is reached
~x
EOD;
$result = false;
if ( preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER) &&
isset(end($matches)['check']) )
$result = array_map(function ($i) { return $i['param']; }, $matches);
else
echo 'bad format' . PHP_EOL;
var_dump($result);
demo
You could split the argument string at ,$ and then append $ back the array values:
$args_array = explode(',$', $arg_str);
foreach($args_array as $key => $arg_raw) {
$args_array[$key] = '$'.ltrim($arg_raw, '$');
}
print_r($args_array);
Output:
(
[0] => $arg1
[1] => $arg2='ABC,DEF'
[2] => $arg3="GHI\",JKL"
[3] => $arg4=array(1,'2)',"3\"),")
)
If you want to use a regex, you can use something like this:
(.+?)(?:,(?=\$)|$)
Working demo
Php code:
$re = '/(.+?)(?:,(?=\$)|$)/';
$str = "\$arg1,\$arg2='ABC,DEF',\$arg3=\"GHI\",JKL\",\$arg4=array(1,'2)',\"3\"),\")\n";
preg_match_all($re, $str, $matches);
Match information:
MATCH 1
1. [0-5] `$arg1`
MATCH 2
1. [6-21] `$arg2='ABC,DEF'`
MATCH 3
1. [22-39] `$arg3="GHI\",JKL"`
MATCH 4
1. [40-67] `$arg4=array(1,'2)',"3\"),")`

Categories