Home > Blockchain >  Multiline to associative array with PHP
Multiline to associative array with PHP

Time:12-01

I have a block of text in a variable $s:

FOO:317263
BAR:abcd
BAZ:s fsiu sfd sdf s dfsdddddd

What's the easiest way (and with good parsing performance) to get an associative array:

$a = array('FOO' => '317263', 'BAR' => 'abcd', 'BAZ' => 's fsiu sfd sdf s dfsdddddd');

?

Up to now, I used:

 preg_match("/^FOO:(.*)$/m", $s, $matches1);
 $a['FOO'] = $matches1[1];
 preg_match("/^BAR:(.*)$/m", $s, $matches1);
 $a['BAR'] = $matches1[1];
 ...

but is there a simpler way to do it?

CodePudding user response:

If it is guaranteed that the values on the right of ':' do not contain ':' or "\n":

$s = <<< STRING
FOO:317263
BAR:abcd
BAZ:s fsiu sfd sdf s dfsdddddd
STRING;

$arr = array_column(array_chunk(preg_split("/[:\n]/", $s), 2), 1, 0);

print_r($arr);

This will print:

Array
(
    [FOO] => 317263
    [BAR] => abcd
    [BAZ] => s fsiu sfd sdf s dfsdddddd
)

CodePudding user response:

You really can use a non-regex approach like

$s = 'FOO:317263
BAR:abcd
BAZ:s fsiu sfd sdf s dfsdddddd';

$a = array();
foreach (explode("\n", $s) as $line) {
    $chnk = explode(':', $line, 2);
    $a[$chnk[0]] = $chnk[1];
}
print_r($a);

After splitting with LF, explode(':', $line, 2); is used to split the line with the first occurrence of a colon.

If you can have different/mixed line endings, replace explode("\n", $s) with preg_split('~\R ~', $s) or even preg_split('~\R ~u', $s) if you deal with Unicode.

If you know you need to do some more matching than you revealed in the question, and you really need a regex, you may consider

$a = array();
if (preg_match_all('~^(\w )\h*:\h*(. )~m', $s, $matches)) {
    $a = array_combine($matches[1],trim($matches[2]));
}
print_r($a);

See the PHP demo and the regex demo. Details:

  • ^ - start of a line (due to m flag)
  • (\w ) - Group 1: one or more word chars
  • \h*:\h* - a colon enclosed with zero or more horizontal whitespaces
  • (. ) - Group 2: the rest of the line.

You can also use parse_ini_string:

$a = parse_ini_string(preg_replace('/^([^:\v]*):(.*)/m', '$1=\'$2\'', $s), FALSE);

See this PHP demo.

The preg_replace('/^([^:\v]*):(.*)/m', '$1=\'$2\'', $s) part replaces all first : chars on each line with a =, wraps the parts after the first : with single quotes (to let parse_ini_string correctly handle additional =s), and parse_ini_string gets the array of keys and values.

CodePudding user response:

Build a stream from your string and read it as a csv:

$s = <<<'DATA'
FOO:317263
BAR:abcd
BAZ:s fsiu sfd sdf s df:sdddddd
DATA;

$handle = fopen("data://text/plain,$s",'r');

$result = [];

while (false !== $cols = fgetcsv($handle, 4096, ':')) {
    $result[array_shift($cols)] = implode(':', $cols);
}

print_r($result);

Always using a stream, but more "functional":

$result = array_reduce(file("data://text/plain,$s"), function($c,$i) {
    [$k, $v] = explode(':', $i, 2);
    $c[$k] = rtrim($v);
    return $c;
}, $c=[]);

print_r($result);
  • Related