How to extract multiple values from a string to call an array? - php

I want to extract values from a string to call an array for basic template functionality:
$string = '... #these.are.words-I_want.to.extract# ...';
$output = preg_replace_callback('~\#([\w-]+)(\.([\w-]+))*\#~', function($matches) {
print_r($matches);
// Replace matches with array value: $these['are']['words-I_want']['to']['extract']
}, $string);
This gives me:
Array
(
[0] => #these.are.words-I_want.to.extract#
[1] => these
[2] => .extract
[3] => extract
)
But I'd like:
Array
(
[0] => #these.are.words-I_want.to.extract#
[1] => these
[2] => are
[3] => words-I_want
[4] => to
[5] => extract
)
Which changes do I need to make to my regex?

It seems that the words are simply dot separated, so match sequences of what you don't want:
preg_replace_callback('/[^#.]+/', function($match) {
// ...
}, $str);
Should give the expected results.
However, if the # characters are the boundary of where the matching should take place, you would need a separate match and then use a simple explode() inside:
preg_replace_callback('/#(.*?)#/', function($match) {
$parts = explode('.', $match[1]);
// ...
}, $str);

You can use array_merge() function to merge the two resulting arrays:
$string = '... #these.are.words-I_want.to.extract# ...';
$result = array();
if (preg_match('~#([^#]+)#~', $string, $m)) {
$result[] = $m[0];
$result = array_merge($result, explode('.', $m[1]));
}
print_r($result);
Output:
Array
(
[0] => #these.are.words-I_want.to.extract#
[1] => these
[2] => are
[3] => words-I_want
[4] => to
[5] => extract
)

Related

Use array_column in combination with preg_match

Lets suppose we have an array of arrays that needs to be converted to rows
From this:
Array
(
[subject] => Array
(
[0] => EDN:LOC:DERR
[1] => EDN:LOC:DOXX
[2] => EDN:LOC:NTTT
[3] => EDN:LOC:NAGA
)
[object] => Array
(
[0] => ABS:D01::ADFPAZ01
[1] => ABS:D01::DOXYWITX
[2] => ABS:D01::NAGBAAD2
[3] => ABS:D01::NAGGAAD2
)
[units] => Array
(
[0] => ABS:D06::UNAA
[1] => ABS:D06::UMMM
[2] => ABS:D06::UPOP
[3] => ABS:D06::UPOP
)
To this:
[0] => "'DERR' , 'ADFPAZ01' , 'UNAA'"
[1] => "'DOXX' , 'DOXYWITX' , 'UMMM'"
[2] => "'NTTT' , 'NAGBAAD2' , 'UPOP'"
[3] => "'NAGA' , 'NAGGAAD2' , 'UPOP'"
So I need the arrays to be cleaned by a pattern and compressed into lines.
I managed the compact view with the following function
$array_res = array();
for ($i=0; $i<=$totalEntries-1; $i++) {
array_push($array_res, implode("', '", array_column($array_of_arrays, $i)));
}
My regex pattern is $pattern = '([^.:]*$)'; And it collects a sequence of letters from the end of the string until it finds a colon. And I used preg_match($pattern, $string, $match) to receive the proper string into the $match variable.
However, I cannot combine the above two procedures either with array_filter or array_map inside the for loop.
EDIT: Note that there can be a subarray that contains values without a colon. In that case we have to get the value as is
[units] => Array
(
[0] => NULL
[1] => VALUE1
[2] => VALUE2
[3] => NULL
)
Rather than using a regex, this just uses array_walk() to process the extracted column and for each item it uses strrchr() with : as the last character to match (although it will include the :, so uses substr() to remove the first char)...
for ($i=0; $i<=$totalEntries-1; $i++) {
$newRow = array_column($array_of_arrays, $i);
array_walk($newRow, function (&$data) {
$data = substr(strrchr(":".$data, ":") , 1);
});
$array_res[] = "'".implode("', '", $newRow)."'";
}
The part ":".$data deals with the time when there is no : in the string, it will always ensure that it does find something to use.
Other way:
$arr = [
'subject' => [ 'EDN:LOC:DERR', 'EDN:LOC:DOXX', 'EDN:LOC:NTTT', 'EDN:LOC:NAGA' ],
'object' => [ 'ABS:D01::ADFPAZ01', 'ABS:D01::DOXYWITX', 'ABS:D01::NAGBAAD2', 'ABS:D01::NAGGAAD2' ],
'units' => [ 'ABS:D06::UNAA', 'ABS:D06::UMMM', 'ABS:D06::UPOP', 'ABS:D06::UPOP' ]
];
$res = [];
$fmt = "'%s', '%s', '%s'";
foreach ($arr['subject'] as $k => $v) {
$res[] = vsprintf($fmt, preg_replace('~^.*:~', '', array_column($arr, $k)));
}
print_r($res);
Notice: If you don't know in advance your array length, nothing forbids to build the format pattern dynamically (using str_repeat or implode).

PHP Explode string between two characters to arrays? (Noob question)

Hello :) I am a beginner in PHP.
I tried several times but did not succeed
I would like to parse a String like :
[1,[01,11,12],[20,21,22]]
to
`
arr[0][0]=>1
arr[1][0]=>01
arr[1][1]=>11
arr[1][2]=>12
arr[2][0]=>20
arr[2][1]=>21
arr[2][2]=>22
`
You can split your string on a comma that is not enclosed by [ and ] using this regex (inspired by this answer) with preg_split:
,(?![^\[]*\])
and then trim surrounding [ and ] from the resultant parts and split those strings on commas into succeeding elements of the output array. For example:
$string = '[1,[01,11,12] ,4 ,5, [20,21,22]]';
$parts = preg_split('/,(?![^\[]*\])/', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
$output = array();
foreach ($parts as $part) {
$part = trim($part, '[] ');
$output[] = explode(',', $part);
}
print_r($output);
Output:
Array
(
[0] => Array
(
[0] => 1
)
[1] => Array
(
[0] => 01
[1] => 11
[2] => 12
)
[2] => Array
(
[0] => 4
)
[3] => Array
(
[0] => 5
)
[4] => Array
(
[0] => 20
[1] => 21
[2] => 22
)
)
Demo on 3v4l.org
If you're 100% certain of the source and safety of the string, you can also just use eval:
eval("\$output = $string;");
The result will be the same.

Convert string with two delimiters into flat associative array [duplicate]

This question already has answers here:
Explode a string to associative array without using loops? [duplicate]
(10 answers)
Closed 7 months ago.
I'm really have no idea about regex...
So I got stuck... Can anyone give me a solution with explanation of regex itself?
Here is my code:
$str = "id:521082299088|name:JOHNSON GREIG DENOIA|mounth:JAN17|amount:170027|admin:2500|billqty:1|metre:R1/900|usage:00010261-00010550|reffno:0BKP21851AF3EC2E0D4F56997EA19DFA|charge:170377|balace:1935";
$pregsplit = preg_split("/[\s|]+/",$string2);
Output:
Array
(
[0] => id:521082299088
[1] => name:JOHNSON
[2] => GREIG
[3] => DENOIA
[4] => mounth:JAN17
[5] => amount:170027
[6] => admin:2500
[7] => billqty:1
[8] => metre:R1/900
[9] => usage:00010261-00010550
[10] => reffno:0BKP21851AF3EC2E0D4F56997EA19DFA
[11] => charge:170377
[12] => balance:1935
)
I want output like this:
Array
(
"id" => 521082299088
"name" => "JOHNSON GREIG DENOIA"
"mount" => "JAN17"
"amount" => 170027
"admin" => 2500
"billqty" => 1
"metre" => "R1/900"
"usage" => "00010261-00010550"
"reffno" => "0BKP21851AF3EC2E0D4F56997EA19DFA"
"charge" => 170377
"balance" => 1935
)
1) The solution using preg_match_all function with specific regex pattern:
$str = "id:521082299088|name:JOHNSON GREIG DENOIA|mounth:JAN17|amount:170027|admin:2500|billqty:1|metre:R1/900|usage:00010261-00010550|reffno:0BKP21851AF3EC2E0D4F56997EA19DFA|charge:170377|balace:1935";
preg_match_all("/(\w+):([^|]+)/", $str, $matches, PREG_SET_ORDER);
$result = [];
foreach ($matches as $items) {
$result[$items[1]] = $items[2];
}
// $items[1] contains a "parameter" name captured by the first capturing group (\w+)
// $items[2] contains a "parameter" value captured by the second capturing group ([^|]+)
print_r($result);
The output:
Array
(
[id] => 521082299088
[name] => JOHNSON GREIG DENOIA
[mounth] => JAN17
[amount] => 170027
[admin] => 2500
[billqty] => 1
[metre] => R1/900
[usage] => 00010261-00010550
[reffno] => 0BKP21851AF3EC2E0D4F56997EA19DFA
[charge] => 170377
[balace] => 1935
)
(\w+) - matches all alphanumeric characters followed by :
([^|]+) - matches all characters excepting | which is delimiter
http://php.net/manual/en/function.preg-match-all.php
2) In addition to the first approach - using array_combine function(to combine all respective values from two capturing groups):
preg_match_all("/(\w+):([^|]+)/", $str, $matches);
$result = array_combine($matches[1], $matches[2]);
// will give the same result
3) The third alternative approach would be using explode() function:
$result = [];
foreach (explode("|", $str) as $items) {
$pair = explode(":", $items);
$result[$pair[0]] = $pair[1];
}
If you are unable to write regular expression.Here is a simple solution using explode() method.The explode() function breaks a string into an array.
<?php
$str = "id:521082299088|name:JOHNSON GREIG DENOIA|mounth:JAN17|amount:170027|admin:2500|billqty:1|metre:R1/900|usage:00010261-00010550|reffno:0BKP21851AF3EC2E0D4F56997EA19DFA|charge:170377|balace:1935";
$array = explode('|',$str);
foreach($array as $key=>$value){
$data = explode(':',$value);
$final[$data[0]] = $data[1];
}
print_r($final);
?>
Output:
Array
(
[id] => 521082299088
[name] => JOHNSON GREIG DENOIA
[mounth] => JAN17
[amount] => 170027
[admin] => 2500
[billqty] => 1
[metre] => R1/900
[usage] => 00010261-00010550
[reffno] => 0BKP21851AF3EC2E0D4F56997EA19DFA
[charge] => 170377
[balace] => 1935
)
To learn more about explode() read docs http://php.net/manual/en/function.explode.php
A funny way (only if your string doesn't contain = or &): translate pipes to ampersands and colons to equal signs, then parse it as an URL query with parse_str:
$str = "id:521082299088|name:JOHNSON GREIG DENOIA|mounth:JAN17|amount:170027|admin:2500|billqty:1|metre:R1/900|usage:00010261-00010550|reffno:0BKP21851AF3EC2E0D4F56997EA19DFA|charge:170377|balace:1935";
parse_str(strtr($str, ':|', '=&'), $result);
print_r($result);
demo
This approach would be an alternative.
You can separate string and create an array from it using PHP's explode() function. Then you can separate the 'key:value' structure using strpos() and substr() functions.
// input string
$str = "id:521082299088|name:JOHNSON GREIG DENOIA|mounth:JAN17|amount:170027|admin:2500|billqty:1|metre:R1/900|usage:00010261-00010550|reffno:0BKP21851AF3EC2E0D4F56997EA19DFA|charge:170377|balace:1935";
// make an array out of the string, split elements on each pipe character ('|')
$arr = explode('|', $str);
// create an output array to keep the results
$output = [];
// process the array
foreach ($arr as $item) {
// get delimiter
$separatorPos = strpos($item, ':');
// take the key part (The part before the ':')
$key = substr($item, 0, $separatorPos);
// take the value part (The part after the ':')
$value = substr($item, $separatorPos);
// push it into the output array
$output[$key] = $value;
}
// dump the output array
var_export($output);
Dump of the output array would be like follwing;
[
'id' => ':521082299088',
'name' => ':JOHNSON GREIG DENOIA',
'mounth' => ':JAN17',
'amount' => ':170027',
'admin' => ':2500',
'billqty' => ':1',
'metre' => ':R1/900',
'usage' => ':00010261-00010550',
'reffno' => ':0BKP21851AF3EC2E0D4F56997EA19DFA',
'charge' => ':170377',
'balace' => ':1935',
]

improve preg / pcre / regex to find PHP variable

String to parse:
$str = "
public $xxxx123;
private $_priv ;
$xxx = 'test';
private $arr_123 = array();
"; // | |
// ^^^^^^^---- get the variable name
What I got so far:
$str = preg_match_all('/\$\S+(;|[[:space:]])/', $str, $matches);
foreach ($matches[0] as $match) {
$match = str_replace('$', '', $match);
$match = str_replace(';', '', $match);
}
It works but I want to know if I can improve the preg, e.g. get rid of the two str_replace and maybe include \t in (;|[[:space:]])
Using a positive lookbehind, you can get only that what you need, to be sure you'll only match valid variable names, I've used this:
preg_match_all('/(?<=\$)[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/',$str,$matches);
var_dump($matches);
which correctly shows:
array (
0 =>
array (
0 => 'xxxx123',
1 => '_priv',
2 => 'xxx',
3 => 'arr_123'
)
)
Which is all you need, no memory waisted on an array containing all variables with their leading and/or trailing chars.
The expression:
(?<=\$) is a positive lookbehind
[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*: is the regex PHP's site suggests themselves on their document pages
simply use backreferences
preg_match_all('/\$(\S+?)[;\s=]/', $str, $matches);
foreach ($matches[1] as $match) {
// $match is now only the name of the variable without $ and ;
}
I changed the regex a little bit, take a look:
$str = '
public $xxxx123;
private $_priv ;
$xxx = "test";
private $arr_123 = array();
';
$matches = array();
//$str = preg_match_all('/\$(\S+)[; ]/', $str, $matches);
$str = preg_match_all('/\$(\S+?)(?:[=;]|\s+)/', $str, $matches); //credits for mr. #booobs for this regex
print_r($matches);
The output:
Array
(
[0] => Array
(
[0] => $xxxx123;
[1] => $_priv
[2] => $xxx
[3] => $arr_123
)
[1] => Array
(
[0] => xxxx123
[1] => _priv
[2] => xxx
[3] => arr_123
)
)
Now you can use the $matches[1] in the foreach loop.
::Update::
After using regex "/\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/" the output looks correct.
String:
$str = '
public $xxxx123; $input1;$input3
private $_priv ;
$xxx = "test";
private $arr_123 = array();
';
And the output:
Array
(
[0] => Array
(
[0] => $xxxx123
[1] => $input1
[2] => $input3
[3] => $_priv
[4] => $xxx
[5] => $arr_123
)
[1] => Array
(
[0] => xxxx123
[1] => input1
[2] => input3
[3] => _priv
[4] => xxx
[5] => arr_123
)
)

Is this possible with preg_match?

i have strings that looks similar like this:
"size:34,35,36,36,37|color:blue,red,white"
is it possible to match all the colors in a preg_match(_all)?
so that i will get "blue", "red" and "white" in the output array?
the colors can be whatever, so i cant go (blue|red|white)
Explode on |
Explode on :
Explode on ,
???
Profit!
Code
IMHO using regular expressions like what's been suggested in the other answers is a much "uglier" solution than something simple like so:
$input = 'size:34,35,36,36,37|color:blue,red,white|undercoating:yes,no,maybe,42';
function get_option($name, $string) {
$raw_opts = explode('|', $string);
$pattern = sprintf('/^%s:/', $name);
foreach( $raw_opts as $opt_str ) {
if( preg_match($pattern, $opt_str) ) {
$temp = explode(':', $opt_str);
return $opts = explode(',', $temp[1]);
}
}
return false; //no match
}
function get_all_options($string) {
$options = array();
$raw_opts = explode('|', $string);
foreach( $raw_opts as $opt_str ) {
$temp = explode(':', $opt_str);
$options[$temp[0]] = explode(',', $temp[1]);
}
return $options;
}
print_r(get_option('undercoating', $input));
print_r(get_all_options($input));
Output:
Array
(
[0] => yes
[1] => no
[2] => maybe
[3] => 42
)
Array
(
[size] => Array
(
[0] => 34
[1] => 35
[2] => 36
[3] => 36
[4] => 37
)
[color] => Array
(
[0] => blue
[1] => red
[2] => white
)
[undercoating] => Array
(
[0] => yes
[1] => no
[2] => maybe
[3] => 42
)
)
You can achieve it in a round about way with preg_match_all() but I'd recommend explode instead.
preg_match_all('/([a-z]+)(?:,|$)/', "size:34,35,36,36,37|color:blue,red,white", $a);
print_r($a[1]);
I think it's possible with lookbehind:
/(?<=(^|\|)color:([^,|],)*)[^,|](?=\||,|$)/g
(for preg_match_all)
Your explode solution is obviously cleaner :-)

Categories