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

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.

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).

how can match some string from content in array (Right to Left)?

I have music chord and lyrics like this :
<p>[Fm]   [Gm]    [Dm]</p>
<p>لورم ایپسوم ، لورم ایپسوم</p>
<p>[A] [Asus4] [Bb]</p>
<p>لورم ایپسوم ، لورم ایپسوم</p>
the "p" tags generated by wordpress editor for each line.
I collect the Chord with brackets by using :
preg_match_all("/\[[^\]]*\]/", $content , $matches);
And result is :
[0] => [Fm]
[1] => [Gm]
[2] => [Dm]
[3] => [A]
[4] => [Asus4]
[5] => [Bb]
but I need to collect the chords from right to left and my array should be like this one :
[0] => [Dm]
[1] => [Gm]
[2] => [Fm]
[3] => [Bb]
[4] => [Asus4]
[5] => [A]
Thanks.
You could try to capture a single line first by matching to: (\[[^\]]*\])+(\<\\p\>){1}
You can then take a matched string and match it against your expression: /\[[^\]]*\]/.
If you take these secondary matches and put them in a 2d array you'll now have a object with this structure:
[0] => [Fm, Gm, Dm]
[1] => [A, Asus4, Bd]
Some (pseudo)code:
$outputarr = [];
preg_match_all("/(\[[^\]]*\])+(\<\\p\>){1}/", $content , $matches);
foreach ($matches as &$value) {
$internalarray = [];
preg_match_all("//\[[^\]]*\]/", $value , $matches2);
foreach ($matches2 as &$value2) {
array_push($internalarray, $value2);
}
array_push($outputarr, $internalarray);
}
You can now call the array_reverse function on elements [0] and [1] to get:
[0] => [Dm, Gm, Fm]
[1] => [Bd, Asus4, A]
And you can finally go from that to a single array by doing this:
$arr = [];
foreach ($outputarr as &$value) {
$arr = array_merge($arr, $value);
}
This should give your wanted inside variable $arr:
[0] => [Dm]
[1] => [Gm]
[2] => [Fm]
[3] => [Bb]
[4] => [Asus4]
[5] => [A]
ps: I haven't taken the whitespace characters into account and ((\[[^\]]*\])|\s)+(\<\\p\>){1} could be the better one. Furthermore this code could be improved by using array_merge and as such bypassing manual array_push
$content = "<p>[Fm] [Gm] [Dm]</p>
<p>لورم ایپسوم ، لورم ایپسوم</p>
<p>[A] [Asus4] [Bb]</p>
<p>لورم ایپسوم ، لورم ایپسوم</p>";
preg_match_all("/<p>.*<\/p>/", $content , $nodes);
$final = [];
foreach ($nodes[0] as $node ) {
if(!empty($node)) {
preg_match_all("/\[[^\]]*\]/", $node , $matches);
if(!empty($matches[0])) {
$final = array_merge($final, array_reverse($matches[0]));
}
}
}
print_r($final);

How to explode a text into an array by end of line and then explode it again by colon using PHP?

I'm trying to read a structured log file, explode it by line and after that by colon.
I'm trying in two diferent approaches fgets and file_get_contents, but in both I'm able to reach only one or the other objective (explode by line or by colon).
Here we have the code:
<?php
// using fgets
$file = fopen('/var/www/html/PU1PAX.log', 'r');
while (!feof($file)) { // while not end of file
$content = fgets($file); // get file's content
$handle = explode(':', $content); // and explode by string
echo '<pre>';
print_r($handle);
echo '</pre>';
}
fclose($file);
// using file_get_contents
$file = '/var/www/html/PU1PAX.log';
$content = file_get_contents($file);
$handle = explode(PHP_EOL, $content);
echo '<pre>';
print_r ($handle);
echo '</pre>';
The first part of the code prints one array to each line:
Array
(
[0] => START-OF-LOG
[1] => 3.0
)
Array
(
[0] => LOCATION
[1] => DX
)
Array
(
[0] => CALLSIGN
[1] => PU1PAX
)
Array
(
[0] => CLUB
[1] =>
)
Array
(
[0] => CONTEST
[1] => CQRJVHF
)
The second part prints only one array with all lines ordered:
Array
(
[0] => START-OF-LOG: 3.0
[1] => LOCATION: DX
[2] => CALLSIGN: PU1PAX
[3] => CLUB:
[4] => CONTEST: CQRJVHF
)
For the first, I would like to find a solution to group all this arrays together, as the content is more or less separated and workable.
For the second, it would be interesting if I could change the array in a way that I could explode it again (this time by colon) to have the first part of the string as the array's keys and the second part as the array's values like the following example:
Array
(
[START-OF-LOG] => 3.0
[LOCATION] => DX
[CALLSIGN] => PU1PAX
[CLUB] =>
[CONTEST] => CQRJVHF
)
But I'm complete stuck... :-(
Use the file() function to read the file line-by-line, then you can explode each line and fill your associative array:
$lines = file('/var/www/html/PU1PAX.log', FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES);
$data = [];
foreach ($lines as $line) {
#list($key, $value) = explode(':', $line, 2);
$data[$key] = trim($value);
}
print_r($data);
Three things:
The third argument to explode() will take care of situations where the value also contains a ":" character
I used the much-hated # error suppression on list() to hide any notices for if a line doesn't contain a ":" character. In that case, trim($value) will give you an empty string as value
That last case is also the reason I included the FILE_IGNORE_NEW_LINES flag. trim($value) would remove it, but if there is no ":" character the newline would be in $key, so better to just not have them in the first place
Maybe you can read character by character fgetc()
$file=fopen("welcome.txt","r") or exit("Unable to open file!");
while ( !feof($file) ) {
$line = "";
do {
$c = fgetc($file);
$line .= $c;
} while ( (!feof($file) and $c != PHP_EOL )
// TODO: process $line (split in columns)
}
fclose($file);
P.s.: I didn't test this code. Just an idea.
//$items = explode(PHP_EOL, $content); in your case
$items = ['START-OF-LOG:3.0','LOCATION:DX','CALLSIGN:PU1PAX','CLUB:','CONTEST:CQRJVHF'];
foreach($items as $item) {
$line = explode(':', $item); // and explode by string
$associtativeArray[$line[0]] = $line[1];
foreach($line as $line_item) {
$oneDimensionalArray[] = $line_item;
}
$groupedArray[] = $line;
}
print_r($associtativeArray);
print_r($oneDimensionalArray);
print_r($groupedArray);
This is the printed result:
// This is $associtativeArray
Array
(
[START-OF-LOG] => 3.0
[LOCATION] => DX
[CALLSIGN] => PU1PAX
[CLUB] =>
[CONTEST] => CQRJVHF
)
// This is $oneDimensionalArray
Array
(
[0] => START-OF-LOG
[1] => 3.0
[2] => LOCATION
[3] => DX
[4] => CALLSIGN
[5] => PU1PAX
[6] => CLUB
[7] =>
[8] => CONTEST
[9] => CQRJVHF
)
// This is $groupedArray
Array
(
[0] => Array
(
[0] => START-OF-LOG
[1] => 3.0
)
[1] => Array
(
[0] => LOCATION
[1] => DX
)
[2] => Array
(
[0] => CALLSIGN
[1] => PU1PAX
)
[3] => Array
(
[0] => CLUB
[1] =>
)
[4] => Array
(
[0] => CONTEST
[1] => CQRJVHF
)
)

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',
]

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