How to get only last bracket from multiple arrays? PHP - php

I have a few arrays and I have to choose only what is in the last bracket. How to do it?
For example my some arrays always be similar, but be different:
Array
(
[0] => 3 BUILTIN\Users:(OI)(CI)(F)
)
Array
(
[0] => BUILTIN\Users:(OI)(CI)(R)
)
Array
(
[0] => 22 BUILTIN\Users:(OI)(CI)(R,W)
)
And i want get result from that like:
(F)
(R)
(R,W)
I must use substr or what?
Regards

You can do this simply with preg_filter
$arr = array(
'3 BUILTIN\Users:(OI)(CI)(F)',
'BUILTIN\Users:(OI)(CI)(R)',
'22 BUILTIN\Users:(OI)(CI)(R,W)'
);
print_r(preg_filter('/^.+(\([^)]+\))$/', '\1', $arr));
Output
Array
(
[0] => (F)
[1] => (R)
[2] => (R,W)
)
Sandbox
The Regex
^ - match start of string
.+ - match anything one or more "greedy"
(...) - First Capture group
\( the ( literally
[^)]+ match anything "not" )
\) the ) literally
$ - match end of string.
So what this does is replace everything in each array item that is not in the capture group with \1 - the first capture group. Which should match everything from the start of the last ( to the end of that "set" ). Basically what we want is only that "stuff" the last parentheses set, which is good, because that's what the above code does (oddly enough, it's like someone set it just the way we need it ... lol).
This should also remove anything from the array that does not match that pattern. For example:
$arr = array(
'3 BUILTIN\Users:(OI)(CI)(F)',
'BUILTIN\Users:(OI)(CI)(R)',
'22 BUILTIN\Users:(OI)(CI)(R,W)',
'foo' //--- foo will not appear in the results, because it does not end with (...)
);
Hope it helps!
preg_filter() is identical to preg_replace() except it only returns the (possibly transformed) subjects where there was a match. For details about how this function works, read the preg_replace() documentation.
https://www.php.net/manual/en/function.preg-filter.php
*PS I gave the above example as it highlights the difference between preg_replace() and preg_filter() (mentioned above). You could do the same with just preg_replace() if you are sure there will always be a match in each item.

Here you can go
$arr = array(
'3 BUILTIN\Users:(OI)(CI)(F)',
'BUILTIN\Users:(OI)(CI)(R)',
'22 BUILTIN\Users:(OI)(CI)(R,W)'
);
$newArr = array();
foreach($arr as $k => $v){
$lastElement = array_filter(explode('(',explode(':',$v)[1]));
$newArr[] = '('.$lastElement[count($lastElement)];
}
print_r($newArr);
Result :-
Array
(
[0] => (F)
[1] => (R)
[2] => (R,W)
)

Related

Get all matches of repeating subgroup [duplicate]

I'm trying to get all substrings matched with a multiplier:
$list = '1,2,3,4';
preg_match_all('|\d+(,\d+)*|', $list, $matches);
print_r($matches);
This example returns, as expected, the last match in [1]:
Array
(
[0] => Array
(
[0] => 1,2,3,4
)
[1] => Array
(
[0] => ,4
)
)
However, I would like to get all strings matched by (,\d+), to get something like:
Array
(
[0] => ,2
[1] => ,3
[2] => ,4
)
Is there a way to do this with a single function such as preg_match_all()?
According to Kobi (see comments above):
PHP has no support for captures of the same group
Therefore this question has no solution.
It's true that PHP (or better to say PCRE) doesn't store values of repeated capturing groups for later access (see PCRE docs):
If a capturing subpattern is matched repeatedly, it is the last portion of the string that it matched that is returned.
But in most cases the known token \G does the job. \G 1) matches the beginning of input string (as \A or ^ when m modifier is not set) or 2) starts match from where the previous match ends. Saying that, you have to use it like the following:
preg_match_all('/^\d+|\G(?!^)(,?\d+)\K/', $list, $matches);
See live demo here
or if capturing group doesn't matter:
preg_match_all('/\G,?\d+/', $list, $matches);
by which $matches will hold this (see live demo):
Array
(
[0] => Array
(
[0] => 1
[1] => ,2
[2] => ,3
[3] => ,4
)
)
Note: the benefit of using \G over the other answers (like explode() or lookbehind solution or just preg_match_all('/,?\d+/', ...)) is that you are able to validate the input string to be only in the desired format ^\d+(,\d+)*$ at the same time while exporting the matches:
preg_match_all('/(?:^(?=\d+(?:,\d+)*$)|\G(?!^),)\d+/', $list, $matches);
Using lookbehind is a way to do the job:
$list = '1,2,3,4';
preg_match_all('|(?<=\d),\d+|', $list, $matches);
print_r($matches);
All the ,\d+ are in group 0.
output:
Array
(
[0] => Array
(
[0] => ,2
[1] => ,3
[2] => ,4
)
)
Splitting is only an option when the character to split isn't used in the patterns to match itself.
I had a situation where a badly formatted comma separated line has to be parsed into any of a number of known options.
i.e. options '1,2', '2', '2,3'
subject '1,2,3'.
Splitting on ',' will result in '1', '2', and '3'; only one ('2') of which is a valid match, this happens because the separator is also part of the options.
The naïve regex would be something like '~^(1,2|2|2,3)(?:,(1,2|2|2,3))*$~i', but this runs into the problem of same-group captures.
My "solution" was to just expand the regex to match the maximum number of matches possible:
'~^(1,2|2|2,3)(?:,(1,2|2|2,3))?(?:,(1,2|2|2,3))?$~i'
(if more options were available, just repeat the '(?:,(1,2|2|2,3))?' bit.
This does result in empty string results for "unused" matches.
It's not the cleanest solution, but works when you have to deal with badly formatted input data.
Why not just:
$ar = explode(',', $list);
print_r($ar);
From http://www.php.net/manual/en/regexp.reference.repetition.php :
When a capturing subpattern is repeated, the value captured is the substring that matched the final iteration.
Also similar thread:
How to get all captures of subgroup matches with preg_match_all()?

preg_split and multiple delimiters

let me start by saying the first number before the first - will be the ID I need to extract. from the first - to the first / will be the 'name' I need to extract. Everything after that I do not care for.
Test String:
1-gc-communications/edit/profile_picture
Expected Output:
Array ( [0] => 1 [1] => gc-communications [2] => /edit/profile_picture )
The best I could come up with was the following patterns (along with their results - with a limit of 3)
Pattern: /-|edit\/profile_picture/
Result: Array ( [0] => 1 [1] => gc [2] => communications/edit/profile_picture )
^ This one is flawed because it does both dashes.
Pattern: /~-~|edit\/profile_picture/
Result: Array ( [0] => 1-gc-communications/ [1] => )
^ major fail.
I know I can do a 2-element limit and just break on the first / and then do a preg_split on the result array, but I would love a way to make this work with one line.
If this is a no-go I am open to other "one liner" solutions.
Try this one
$str = '1-gc-communications/edit/profile_picture';
$match = preg_split('#([^-]+)-([^/]+)/(.*)#', $str, 0, PREG_SPLIT_DELIM_CAPTURE);
print_r($match);
return like as
array (
0 => '',
1 => '1',
2 => 'gc-communications',
3 => 'edit/profile_picture',
4 => '',
)
the first number before the first - will be the ID I need to extract. from the first - to the first / will be the 'name' I need to extract. Everything after that I do not care for.
This task seems a great candidate for sscanf() -- it is specifically designed for parsing (scanning) a formatted string. Not only is the syntax brief, you know that you do not need to make repeated matches with the pattern. The output, in case it matters, can be pre-cast as an integer or string for convenience. The remaining string from the first occurring slash are simply ignored.
Code: (Demo)
$str = '1-gc-communications/edit/profile_picture';
var_export(
sscanf($str, '%d-%[^/]')
# ^^ ^^^^^- greedily match one or more non-slash characters
# ^^------- greedily match one or more numeric characters
);
Output:
array (
0 => 1, #<-- integer-typed
1 => 'gc-communications', #<-- string-typed
)

Splitting GET string

I need to split my GET string into some array. The string looks like this:
ident[0]=<IDENT_0>&value[0]=<VALUE_0>&version[0]=<VERSION_0>&....&ident[N]=<IDENT_N>&value[N]=<VALUE_N>&version[N]=<VERSION_N>
So, I need to split this string by every third ampersand character, like this:
ident[0]=<IDENT_0>&value[0]=<VALUE_0>&version[0]=<VERSION_0>
ident[1]=<IDENT_1>&value[1]=<VALUE_1>&version[1]=<VERSION_1> and so on...
How can I do it? What regular expression should I use? Or is here some better way to do it?
There is a better way (assuming this is data being sent to your PHP page, not some other thing you're dealing with).
PHP provides a "magic" array called $_GET which already has the values parsed out for you.
For example:
one=1&two=2&three=3
Would result in this array:
Array ( [one] => 1 [two] => 2 [three] => 3 )
So you could access the variables like so:
$oneValue = $_GET['one']; // answer is 1
$twoValue = $_GET['two']; // and so on
If you provide array indexes, which your example does, it'll sort those out for you as well. So, to use your example above $_GET would look like:
Array
(
[ident] => Array
(
[0] => <IDENT_0>
[N] => <IDENT_N>
)
[value] => Array
(
[0] => <VALUE_0>
[N] => <VALUE_N>
)
[version] => Array
(
[0] => <VERSION_0>
[N] => <VERSION_N>
)
)
I'd assume your N keys will actually be numbers, so you'll be able to look them up like so:
$_GET['ident'][0] // => <IDENT_0>
$_GET['value'][0] // => <VALUE_0>
$_GET['version'][0] // => <VERSION_0>
You could loop across them all or whatever, and you will never have to worry about splitting them all out yourself.
Hope it helps you.
You can use preg_split with this pattern: &(?=ident)
$result = preg_split('~&(?=ident)~', $yourstring);
regex detail: &(?=ident) means & followed by ident
(?=..) is a lookahead assertion that performs only a check but match nothing.
Or using preg_match_all:
preg_match_all('~(?<=^|&)[^&]+&[^&]+&[^&]+(?=&|$)~', $yourstring, &matches);
$result = $matches[0];
pattern detail: (?<=..) is a lookbehind assertion
(?<=^|&) means preceded by the begining of the string ^ or an ampersand.
[^&]+ means all characters except the ampersand one or more times.
(?=&|$) means followed by an ampersand or the end of the string $.
Or you can use explode, and then a for loop:
$items = explode('&', $yourstring);
for ( $i=0; $i<sizeof($items); $i += 3 ) {
$result[] = implode('&', array_slice($items, $i, 3));
}

Regular Expression to get full string if it doesn't contain a specific character?

I need help with a regular expression that will find matches in the strings below:
myDOG_test
myCAT_test
Basically, I want to return 'DOG' or 'CAT' from these paths.
Then I have similar strings (all start with 'my') that don't contain the underscore AFTER the value I want, and in that case I just want to return the FULL string -- in a match group.
myCentralReports
myDEMO3
This is the REGEXP that I have so far:
.*?my(.*?)\_.*
This correctly puts CAT & DOG in the matching group, but I'm having problems matching the other 2 strings. Obviously I left the hardcoded underscore in there just to show you what I started with -- but I need to modify this for the other case. Any help is appreciated! Thanks.
Not sure why you need regex:
explode('_', $string);
First element will contain myDOG or myDEMO3. Remove 'my' if needed, it's not clear whether you want 'my' in your second case.
'/\smy(.+?)[_|\s]/'
This will get anything between a whitespace character followed by "my", and the next trailing underscore or whitespace character. try it out.
You could do :
$list = array(' myDOG_test', 'myCAT_test',' myCentralReports', 'myDEMO3');
foreach($list as $elem) {
preg_match("/^\s*my(.+?)(?:_|$)/", $elem, $m);
echo "$elem : matches = ";print_r($m);
}
Output:
myDOG_test : matches = Array
(
[0] => myDOG_
[1] => DOG
)
myCAT_test : matches = Array
(
[0] => myCAT_
[1] => CAT
)
myCentralReports : matches = Array
(
[0] => myCentralReports
[1] => CentralReports
)
myDEMO3 : matches = Array
(
[0] => myDEMO3
[1] => DEMO3
)

Get position of all matches in group

Consider the following example:
$target = 'Xa,a,aX';
$pattern = '/X((a),?)*X/';
$matches = array();
preg_match_all($pattern,$target,$matches,PREG_OFFSET_CAPTURE|PREG_PATTERN_ORDER);
var_dump($matches);
What it does is returning only the last 'a' in the series, but what I need is all the 'a's.
Particularly, I need the position of ALL EACH OF the 'a's inside the string separately, thus PREG_OFFSET_CAPTURE.
The example is much more complex, see the related question: pattern matching an array, not their elements per se
Thanks
It groups a single match since the regex X((a),?)*X matches the entire string. The last ((a),?) will be grouped.
What you want to match is an a that has an X before it (and the start of the string), has a comma ahead of it, or has an X ahead of it (and the end of the string).
$target = 'Xa,a,aX';
$pattern = '/(?<=^X)a|a(?=X$|,)/';
preg_match_all($pattern, $target, $matches, PREG_OFFSET_CAPTURE);
print_r($matches);
Output:
Array
(
[0] => Array
(
[0] => Array
(
[0] => a
[1] => 1
)
[1] => Array
(
[0] => a
[1] => 3
)
[2] => Array
(
[0] => a
[1] => 5
)
)
)
When your regex includes X, it matches once. It finds one large match with groups in it. What you want is many matches, each with its own position.
So, in my opinion the best you can do is simply search for /a/ or /a,?/ without any X. Then matches[0] will contain all appearances of 'a'
If you need them between X, pre-select this part of the string.

Categories