Parsing PHP strings with quoted values - php

I'd like to parse a string like the following :
'serviceHits."test_server"."http_test.org" 31987'
into an array like :
[0] => serviceHits
[1] => test_server
[2] => http_test.org
[3] => 31987
Basically I want to split in dots and spaces, treating strings within quotes as a single value.
The format of this string is not fixed, this is just one example. It might contain different numbers of elements with quoted and numerical elements in different places.
Other strings might look like :
test.2 3 which should parse to [test|2|3]
test."342".cake.2 "cheese" which should parse to [test|342|cake|2|cheese]
test."red feet".3."green" 4 which should parse to [test|red feet|3|green|4]
And sometimes the oid string may contain a quote mark, which should be included if possible, but it's the least important part of the parser:
test."a \"b\" c" "cheese face" which should parse to [test|a "b" c|cheese face]
I'm trying to parse SNMP OID strings from agent written by people with quite varying ideas on what an OID should look like, in a generic manner.
Parsing off the oid string (the bit separated with dots) return value (the last value) into separate named arrays would be nice. Simply splitting on space before parsing the string wouldn't work, as both the OID and the value can contain spaces.
Thanks!

I agree this can be hard to find one regexp to resolve this issue.
Here's a complete solution :
$results = array();
$str = 'serviceHits."test_\"server"."http_test.org" 31987';
// Encode \" to something else temporary
$str_encoded_quotes = strtr($str,array('\\"'=>'####'));
// Split by strings between double-quotes
$str_arr = preg_split('/("[^"]*")/',$str_encoded_quotes,-1,PREG_SPLIT_DELIM_CAPTURE);
foreach ($str_arr as $substr) {
// If value is a dot or a space, do nothing
if (!preg_match('/^[\s\.]$/',$substr)) {
// If value is between double-quotes, it's a string
// Return as is
if (preg_match('/^"(.*)"$/',$substr)) {
$substr = preg_replace('/^"(.*)"$/','\1',$substr); // Remove double-quotes around
$results[] = strtr($substr,array('####'=>'"')); // Get escaped double-quotes back inside the string
// Else, it must be splitted
} else {
// Split by dot or space
$substr_arr = preg_split('/[\.\s]/',$substr,-1,PREG_SPLIT_NO_EMPTY);
foreach ($substr_arr as $subsubstr)
$results[] = strtr($subsubstr,array('####'=>'"')); // Get escaped double-quotes back inside string
}
}
// Else, it's an empty substring
}
var_dump($results);
Tested with all of your new string examples.
First attempt (OLD)
Using preg_split :
$str = 'serviceHits."test_server"."http_test.org" 31987';
// -1 : no limit
// PREG_SPLIT_NO_EMPTY : do not return empty results
preg_split('/[\.\s]?"[\.\s]?/',$str,-1,PREG_SPLIT_NO_EMPTY);

The easiest way is probably to replace dots and spaces inside strings with placeholders, split, then remove the placeholders. Something like this:
$in = 'serviceHits."test_server"."http_test.org" 31987';
$a = preg_replace_callback('!"([^"]*)"!', 'quote', $in);
$b = preg_split('![. ]!', $a);
foreach ($b as $k => $v) $b[$k] = unquote($v);
print_r($b);
# the functions that do the (un)quoting
function quote($m){
return str_replace(array('.',' '),
array('PLACEHOLDER-DOT', 'PLACEHOLDER-SPACE'), $m[1]);
}
function unquote($str){
return str_replace(array('PLACEHOLDER-DOT', 'PLACEHOLDER-SPACE'),
array('.',' '), $str);
}

Here is a solution that works with all of your test samples (plus one of my own) and allows you to escape quotes, dots, and spaces.
Due to the requirement of handling escape codes, a split is not really possible.
Although one can imagine a regex that matches the entire string with '()' to mark the separate elements, I was unable to get it working using preg_match or preg_match_all.
Instead I parsed the string incrementally, pulling off one element at a time. I then use stripslashes to unescape quotes, spaces, and dots.
<?php
$strings = array
(
'serviceHits."test_server"."http_test.org" 31987',
'test.2 3',
'test."342".cake.2 "cheese"',
'test."red feet".3."green" 4',
'test."a \\"b\\" c" "cheese face"',
'test\\.one."test\\"two".test\\ three',
);
foreach ($strings as $string)
{
print"'{$string}' => " . print_r(parse_oid($string), true) . "\n";
}
/**
* parse_oid parses and OID and returns an array of the parsed elements.
* This is an all-or-none function, and will return NULL if it cannot completely
* parse the string.
* #param string $string The OID to parse.
* #return array|NULL A list of OID elements, or null if error parsing.
*/
function parse_oid($string)
{
$result = array();
while (true)
{
$matches = array();
$match_count = preg_match('/^(?:((?:[^\\\\\\. "]|(?:\\\\.))+)|(?:"((?:[^\\\\"]|(?:\\\\.))+)"))((?:[\\. ])|$)/', $string, $matches);
if (null !== $match_count && $match_count > 0)
{
// [1] = unquoted, [2] = quoted
$value = strlen($matches[1]) > 0 ? $matches[1] : $matches[2];
$result[] = stripslashes($value);
// Are we expecting any more parts?
if (strlen($matches[3]) > 0)
{
// I do this (vs keeping track of offset) to use ^ in regex
$string = substr($string, strlen($matches[0]));
}
else
{
return $result;
}
}
else
{
// All or nothing
return null;
}
} // while
}
This generates the following output:
'serviceHits."test_server"."http_test.org" 31987' => Array
(
[0] => serviceHits
[1] => test_server
[2] => http_test.org
[3] => 31987
)
'test.2 3' => Array
(
[0] => test
[1] => 2
[2] => 3
)
'test."342".cake.2 "cheese"' => Array
(
[0] => test
[1] => 342
[2] => cake
[3] => 2
[4] => cheese
)
'test."red feet".3."green" 4' => Array
(
[0] => test
[1] => red feet
[2] => 3
[3] => green
[4] => 4
)
'test."a \"b\" c" "cheese face"' => Array
(
[0] => test
[1] => a "b" c
[2] => cheese face
)
'test\.one."test\"two".test\ three' => Array
(
[0] => test.one
[1] => test"two
[2] => test three
)

Related

PHP how to avoid mixed letter-number when extracting chunks of numbers from string

I'm writing a PHP function to extract numeric ids from a string like:
$test = '123_123_Foo'
At first I took two different approaches, one with preg_match_all():
$test2 = '123_1256_Foo';
preg_match_all('/[0-9]{1,}/', $test2, $matches);
print_r($matches[0]); // Result: 'Array ( [0] => 123 [1] => 1256 )'
and other with preg_replace() and explode():
$test = preg_replace('/[^0-9_]/', '', $test);
$output = array_filter(explode('_', $test));
print_r($output); // Results: 'Array ( [0] => 123 [1] => 1256 )'
Any of them works well as long as the string does not content mixed letters and numbers like:
$test2 = '123_123_234_Foo2'
The evident result is Array ( [0] => 123 [1] => 1256 [2] => 2 )
So I wrote another regex to get rid off of mixed strings:
$test2 = preg_replace('/([a-zA-Z]{1,}[0-9]{1,}[a-zA-Z]{1,})|([0-9]{1,}[a-zA-Z]{1,}[0-9]{1,})|([a-zA-Z]{1,}[0-9]{1,})|([0-9]{1,}[a-zA-Z]{1,})|[^0-9_]/', '', $test2);
$output = array_filter(explode('_', $test2));
print_r($output); // Results: 'Array ( [0] => 123 [1] => 1256 )'
The problem is evident too, more complicated paterns like Foo2foo12foo1 would pass the filter. And here's where I got a bit stuck.
Recap:
Extract a variable ammount of chunks of numbers from string.
The string contains at least 1 number, and may contain other numbers
and letters separated by underscores.
Only numbers not preceded or followed by letters must be extracted.
Only the numbers in the first half of the string matter.
Since only the first half is needed I decided to split in the first occurrence of letter or mixed number-letter with preg_split():
$test2 = '123_123_234_1Foo2'
$output = preg_split('/([0-9]{1,}[a-zA-Z]{1,})|[^0-9_]/', $test, 2);
preg_match_all('/[0-9]{1,}/', $output[0], $matches);
print_r($matches[0]); // Results: 'Array ( [0] => 123 [1] => 123 [2] => 234 )'
The point of my question is if is there a simpler, safer or more efficient way to achieve this result.
If I understand your question correctly, you want to split an underscore-delimited string, and filter out any substrings that are not numeric. If so, this can be achieved without regex, with explode(), array_filter() and ctype_digit(); e.g:
<?php
$str = '123_123_234_1Foo2';
$digits = array_filter(explode('_', $str), function ($substr) {
return ctype_digit($substr);
});
print_r($digits);
This yields:
Array
(
[0] => 123
[1] => 123
[2] => 234
)
Note that ctype_digit():
Checks if all of the characters in the provided string are numerical.
So $digits is still an array of strings, albeit numeric.
Hope this helps :)
Getting just the numeric part of the string after the explode
$test2 = "123_123_234_1Foo2";
$digits = array_filter(explode('_', $test2 ), 'is_numeric');
var_dump($digits);
Result
array(3) { [0]=> string(3) "123" [1]=> string(3) "123" [2]=> string(3) "234" }
Use strtok
Regex isn't a magic bullet, and there are FAR simpler fixes for your problem, especially considering you're trying to split on a delimiter.
Any of the following approaches would be cleaner, and more maintainable, and the strtok() approach would probably perform better:
Use explode to create and loop through an array, checking each value.
Use preg_split to do the same, but with more a adaptable approach.
Use strtok, as it is designed exactly for this use-case.
Basic exmple for your case:
function strGetInts(string $str, str $delim) {
$word = strtok($str, $delim);
while (false !== $word) {
if (is_integer($word) {
yield (int) $word;
}
$word = strtok($delim);
}
}
$test2 = '123_1256_Foo';
foreach(strGetInts($test2, '_-') as $key {
print_r($key);
}
Note: the second argument to strtok is string containing ANY delimiter to split the string on. Thus, my example will group results into strings separated by underscores or dashes.
Additional Note: If and only if the string only needs to be split on a single delimiter (underscore only), a method using explode will likely result in better performance. For such a solution, see the other answer in this thread: https://stackoverflow.com/a/46937452/1589379 .

Most elegant way to clean a string into only comma separated numerals

After instructing clients to input only
number comma number comma number
(no set length, but generally < 10), the results of their input have been, erm, unpredictable.
Given the following example input:
3,6 ,bannana,5,,*,
How could I most simply, and reliably end up with:
3,6,5
So far I am trying a combination:
$test= trim($test,","); //Remove any leading or trailing commas
$test= preg_replace('/\s+/', '', $test);; //Remove any whitespace
$test= preg_replace("/[^0-9]/", ",", $test); //Replace any non-number with a comma
But before I keep throwing things at it...is there an elegant way, probably from a regex boffin!
In a purely abstract sense this is what I'd do:
$test = array_filter(array_map('trim',explode(",",$test)),'is_numeric')
Example:
http://sandbox.onlinephpfunctions.com/code/753f4a833e8ff07cd9c7bd780708f7aafd20d01d
<?php
$str = '3,6 ,bannana,5,,*,';
$str = explode(',', $str);
$newArray = array_map(function($val){
return is_numeric(trim($val)) ? trim($val) : '';
}, $str);
print_r(array_filter($newArray)); // <-- this will give you array
echo implode(',',array_filter($newArray)); // <--- this give you string
?>
Here's an example using regex,
$string = '3,6 ,bannana,5,-6,*,';
preg_match_all('#(-?[0-9]+)#',$string,$matches);
print_r($matches);
will output
Array
(
[0] => Array
(
[0] => 3
[1] => 6
[2] => 5
[3] => -6
)
[1] => Array
(
[0] => 3
[1] => 6
[2] => 5
[3] => -6
)
)
Use $matches[0] and you should be on your way.
If you don't need negative numbers just remove the first bit in the in the regex rule.

PHP: split a string of alternating groups of characters into an array

I have a string whose correct syntax is the regex ^([0-9]+[abc])+$. So examples of valid strings would be: '1a2b' or '00333b1119a555a0c'
For clarity, the string is a list of (value, letter) pairs and the order matters. I'm stuck with the input string so I can't change that. While testing for correct syntax seems easy in principle with the above regex, I'm trying to think of the most efficient way in PHP to transform a compliant string into a usable array something like this:
Input:
'00333b1119a555a0c'
Output:
array (
0 => array('num' => '00333', 'let' => 'b'),
1 => array('num' => '1119', 'let' => 'a'),
2 => array('num' => '555', 'let' => 'a'),
3 => array('num' => '0', 'let' => 'c')
)
I'm having difficulty using preg_match for this. For example this doesn't give the expected result, the intent being to greedy-match on EITHER \d+ (and save that) OR [abc] (and save that), repeated until end of string reached.
$text = '00b000b0b';
$out = array();
$x = preg_match("/^(?:(\d+|[abc]))+$/", $text, $out);
This didn't work either, the intent here being to greedy-match on \d+[abc] (and save these), repeated until end of string reached, and split them into numbers and letter afterwards.
$text = '00b000b0b';
$out = array();
$x = preg_match("/^(?:\d+[abc])+$/", $text, $out);
I'd planned to check syntax as part of the preg_match, then use the preg_match output to greedy-match the 'blocks' (or keep the delimiters if using preg_split), then if needed loop through the result 2 items at a time using for (...; i+=2) to extract value-letter in their pairs.
But I can't seem to even get that basic preg_split() or preg_match() approach to work smoothly, much less explore if there's a 'neater' or more efficient way.
Your regex needs a few matching groups
/([0-9]+?)([a-z])/i
This means match all numbers in one group, and all letters in another. Preg match all gets all matches.
The key to the regex is the non greedy flag ? which matches the shortest possible string.
match[0] is the whole match
match[1] is the first match group (the numbers)
match[2] is the second match group (the letter)
example below
<?php
$input = '00333b1119a555a0c';
$regex = '/([0-9]+?)([a-z])/i';
$out = [];
$parsed = [];
if (preg_match_all($regex, $input, $out)) {
foreach ($out[0] as $index => $value) {
$parsed[] = [
'num' => $out[1][$index],
'let' => $out[2][$index],
];
}
}
var_dump($parsed);
output
array(4) {
[0] =>
array(2) {
'num' =>
string(5) "00333"
'let' =>
string(1) "b"
}
[1] =>
array(2) {
'num' =>
string(4) "1119"
'let' =>
string(1) "a"
}
[2] =>
array(2) {
'num' =>
string(3) "555"
'let' =>
string(1) "a"
}
[3] =>
array(2) {
'num' =>
string(1) "0"
'let' =>
string(1) "c"
}
}
Simple solution with preg_match_all(with PREG_SET_ORDER flag) and array_map functions:
$input = '00333b1119a555a0c';
preg_match_all('/([0-9]+?)([a-z]+?)/i', $input, $matches, PREG_SET_ORDER);
$result = array_map(function($v) {
return ['num' => $v[1], 'let' => $v[2]];
}, $matches);
print_r($result);
The output:
Array
(
[0] => Array
(
[num] => 00333
[let] => b
)
[1] => Array
(
[num] => 1119
[let] => a
)
[2] => Array
(
[num] => 555
[let] => a
)
[3] => Array
(
[num] => 0
[let] => c
)
)
You can use:
$str = '00333b1119a555a0c';
$arr=array();
if (preg_match_all('/(\d+)(\p{L}+)/', $str, $m)) {
array_walk( $m[1], function ($v, $k) use(&$arr, $m ) {
$arr[] = [ 'num'=>$v, 'let'=>$m[2][$k] ]; });
}
print_r($arr);
Output:
Array
(
[0] => Array
(
[num] => 00333
[let] => b
)
[1] => Array
(
[num] => 1119
[let] => a
)
[2] => Array
(
[num] => 555
[let] => a
)
[3] => Array
(
[num] => 0
[let] => c
)
)
All of the above work. But they didn't seem to have the elegance I wanted - they needed to loop, use array mapping, or (for preg_match_all()) they needed another almost identical regex as well, just to verify the string matched the regex.
I eventually found that preg_match_all() combined with named captures solved it for me. I hadn't used named captures for that purpose before and it looks powerful.
I also added an optional extra step to simplify the output if dups aren't expected (which wasn't in the question but may help someone).
$input = '00333b1119a555a0c';
preg_match_all("/(?P<num>\d+)(?P<let>[dhm])/", $input, $raw_matches, PREG_SET_ORDER);
print_r($raw_matches);
// if dups not expected this is also worth doing
$matches = array_column($raw_matches, 'num', 'let');
print_r($matches);
More complete version with input+duplicate checking
$input = '00333b1119a555a0c';
if (!preg_match("/^(\d+[abc])+$/",$input)) {
// OPTIONAL: detected $input incorrectly formatted
}
preg_match_all("/(?P<num>\d+)(?P<let>[dhm])/", $input, $raw_matches, PREG_SET_ORDER);
$matches = array_column($raw_matches, 'num', 'let');
if (count($matches) != count($raw_matches)) {
// OPTIONAL: detected duplicate letters in $input
}
print_r($matches);
Explanation:
This uses preg_match_all() as suggested by #RomanPerekhrest and #exussum to break out the individual groups and split the numbers and letters. I used named groups so that the resulting array of $raw_matches is created with the correct names already.
But if dups arent expected, then I used an extra step with array_column(), which directly extracts data from a nested array of entries and creates a desired flat array, without any need for loops, mapping, walking, or assigning item by item: from
(group1 => (num1, let1), group2 => (num2, let2), ... )
to the "flat" array:
(let1 => num1, let2 => num2, ... )
If named regex matches feels too advanced then they can be ignored - the matches will be given numbers anyway and this will work just as well, you would have to manually assign letters and it's just harder to follow.
preg_match_all("/(\d+)([dhm])/", $input, $raw_matches, PREG_SET_ORDER);
$matches = array_column($raw_matches, 1, 2);
If you need to check for duplicated letters (which wasn't in the question but could be useful), here's how: If the original matches contained >1 entry for any letter then when array_column() is used this letter becomes a key for the new array, and duplicate keys can't exist. Only one entry for each letter gets kept. So we just test whether the number of matches originally found, is the same as the number of matches in the final array after array_coulmn. If not, there were duplicates.

PHP preg_match_all expression

I have virtually no experience of regx, but trying my best.
I have a string like this:
$fString = "Name=Sök,Value=2,Title=Combine me,Options=[Item1=1,Item2=2,Item3=3]";
I want to get an array looking like this:
Array[0] = "Name=Sök"
Array[1] = "Value=2"
Array[2] = "Title=Combine me"
Array[3] = "Options=[Item1=1,Item2=2,Item3=3]"
What I have managed to do so far is:
preg_match_all("/[^,]*[\w\d]*=[^,]*/",$fString,$Data);
But it I can't figure out how to fix the last "Option".
Array ( [0] => Array ( [0] => Name=S�k [1] => Value=2 [2] => Title=Combine me [3] => Options=[Item1=1 [4] => Item2=2 [5] => Item3=3] ) )
...and why is the result an array inside an array?!?
[EDIT]
I guess I need to explain the whole idea of what I'm trying to do here, I'm not sure I'm on the right track any more.
I have created some classes where I store all the "persistent" variables in an array. I have a function that serializes this array so I can be stored in a database.
I know all about the serialize() function, but I'm doing some filtering so I can't use it as it is, and I also prefer to have it more readable for manual editing. This array can have nested arrays within, that needs to be preserved. When I read it all back from the database, the original array must be created again.
I had it all working with the eval() command but stumbled into trouble where I had nested arrays because of the " or ' characters was breaking the main outer string. So this approach was an attempt to serialize everything without nested strings that needed to be preserved.
So if I can solve the nested data with preg_match_all I'm there, otherwise I need to come up with another solution.
I guess the data needs to be escaped as well, such as the , and [ ]
Here is a function that will do basically what you need:
function explode_me($str) {
$a = array();
$v = "";
$ignore = false;
for ($i = 0; $i < strlen($str); $i++) {
if ($str[$i] == ',' && !$ignore) {
$a[] = $v;
$v = "";
}
else if ($str[$i] == '[' && !$ignore) {
$ignore = true;
$v .= $str[$i];
}
else if ($str[$i] == ']' && $ignore) {
$ignore = false;
$v .= $str[$i];
}
else {
$v .= $str[$i];
}
}
$a[] = $v;
return $a;
}
To test it:
$str = "Name=Sök,Value=2,Title=Combine me,Options=[Item1=1,Item2=2,Item3=3]";
$a = explode_me($str);
print_r($a);
which prints:
Array
(
[0] => Name=Sök
[1] => Value=2
[2] => Title=Combine me
[3] => Options=[Item1=1,Item2=2,Item3=3]
)
(\w+)=(\[[^\]]+\]|[^,]+)
This breaks down as:
(\w+) # a word (store in match group 1)
= # the "=" character
( # begin match group 2
\[ # a "[" character
[^\]]+ # anything but "]" character
\] # a "]" character
| # or...
[^,]+ # anything but a comma
) # end match group 1
Apply with preg_match_all():
$fString = "Name=Sök,Value=2,Title=Combine me,Options=[Item1=1,Item2=2,Item3=3]";
$matches = array();
preg_match_all("/(\\w+)=(\\[[^\\]]+\\]|[^,]+)/", $fString, $matches);
Which results in something even more detailed than you wanted to have:
Array
(
[0] => Array
(
[0] => Name=Sök
[1] => Value=2
[2] => Title=Combine me
[3] => Options=[Item1=1,Item2=2,Item3=3]
)
[1] => Array
(
[0] => Name
[1] => Value
[2] => Title
[3] => Options
)
[2] => Array
(
[0] => Sök
[1] => 2
[2] => Combine me
[3] => [Item1=1,Item2=2,Item3=3]
)
)
$result[0] is what you wanted. $result[1] and $result[2] are property names and values separately, which enables you to use them right away instead of making an extra step that splits things like "Options=[Item1=1,Item2=2,Item3=3]" at the correct =.
If you could change the separators between the items (where it says Item1=1,Item2=2,Item3=3 to something like Item1=1|Item2=2|Item3=3) you could easily use explode(',',$fString) to convert a string to an array.
I can also offer this piece of code that will change the separators, as I have no experience with regex:
$newstr = str_replace(',Item','|Item',$fString);
$newarray = explode(',',$newstr);
$newarray will look like this:
Array[0] = "Name=Sök"
Array[1] = "Value=2"
Array[2] = "Title=Combine me"
Array[3] = "Options=[Item1=1|Item2=2|Item3=3]"
This is a problem that lends itself more to parsing than regex extraction. Bout you can separate the special case to make it work:
preg_match_all("/(\w+)=( \w[^,]+ | \[[^\]]+\] )/x", $str, $m);
$things = array_combine($m[1], $m[2]);
Will give you a PHP variable like (but you can access $m[0] for the unparsed strings):
[Name] => Sök
[Title] => Combine me
[Options] => [Item1=1,Item2=2,Item3=3]
You can reapply the function on Options to explode that too.
The trick again is differentiating between \w anything that starts with a letter, and the \[...\] enclosed options. There you have to just make it match ^] all non-closing-brackets, and that's it.
So, here is another approach. It's a mini parser for nested structures. Adapt the regex if you need escape codes.
function parse(&$s) {
while (strlen($s) && preg_match("/^(.*?)([=,\[\]])/", $s, $m)) {
$s = substr($s, 1 + strlen($m[1]));
switch ($m[2]) {
case "=":
$key = $m[1];
break;
case ",":
if (!isset($r[$key])) {
$r[$key] = $m[1];
}
break;
case "[":
$r[$key] = parse($s);
break;
case "]":
return $r;
}
}
if ($s) { $r[$key] = $s; } // remainder
return $r;
}

How go get everything from between parenthesis in PHP?

Array(
[1] => put returns (between) paragraphs
[2] => (for) linebreak (add) 2 spaces at end
[3] => indent code by 4 (spaces!)
[4] => to make links
)
Want to get text inside brackets (for each value):
take only first match
remove this match from the value
write all matches to new array
After function arrays should look like:
Array(
[1] => put returns paragraphs
[2] => linebreak (add) 2 spaces at end
[3] => indent code by 4
[4] => to make links
)
Array(
[1] => between
[2] => for
[3] => spaces!
[4] =>
)
What is the solution?
I would use the regular expression /\((\([^()]*\)|[^()]*)\)/ (this will match one or two pairs of parentheses) together with preg_split:
$matches = array();
foreach ($arr as &$value) {
$parts = preg_split('/\((\([^()]*\)|[^()]*)\)/', $value, 2, PREG_SPLIT_DELIM_CAPTURE);
if (count($parts) > 1) {
$matches[] = current(array_splice($parts, 1, 1));
$value = implode('', $parts);
}
}
Using preg_split with PREG_SPLIT_DELIM_CAPTURE flag set will contain the matched separators in the result array. So a match was found, there are at least three parts. In that case the second member is the one we are looking for. That member is removed with array_splice that does also return the array of removed members. To get the removed member, current is used on the return value of array_splice. The remaining members are then put back together.
Assuming you meant (between) and not ((between))
$arr = array(
0 => 'put returns (between) paragraphs',
1 => '(for) linebreak (add) 2 spaces at end',
2 => 'indent code by 4 (spaces!)',
3 => 'to make links');
var_dump($arr);
$new_arr = array();
foreach($arr as $key => &$str) {
if(preg_match('/(\(.*?\))/',$str,$m)) {
$new_arr[] = $m[1];
$str = preg_replace('/\(.*?\)/','',$str,1);
}
else {
$new_arr[] = '';
}
}
var_dump($arr);
var_dump($new_arr);
Working link

Categories