【问题标题】:Multiline to associative array with PHP多行到 PHP 的关联数组
【发布时间】:2022-01-07 09:10:42
【问题描述】:

我在变量$s中有一段文本:

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

获取关联数组的最简单方法(并且具有良好的解析性能)是什么:

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

?

到目前为止,我使用的是:

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

但是有没有更简单的方法呢?

【问题讨论】:

  • 我会使用explode(): 1. 按换行符分解,然后 2. 循环遍历结果并按: 分解并将其放入关联数组中。
  • 如果数组键有重复怎么办? (因为关联数组可能会中断)
  • @nice_dev 键中没有重复项,如果发生这种情况,如果有问题我没关系,因为无论如何都不应该发生。
  • @Basj 好吧。

标签: php arrays parsing preg-match associative-array


【解决方案1】:

你真的可以使用像

这样的非正则表达式方法
$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);

用LF分割后,explode(':', $line, 2);用于分割第一次出现冒号的行。

如果您可以使用不同/混合的行尾,请将 explode("\n", $s) 替换为 preg_split('~\R+~', $s),如果您处理 Unicode,则甚至替换为 preg_split('~\R+~u', $s)

如果你知道你需要做比你在问题中透露的更多的匹配,并且你真的需要一个正则表达式,你可以考虑

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

请参阅 PHP demoregex demo详情

  • ^ - 行首(由于m 标志)
  • (\w+) - 第 1 组:一个或多个单词字符
  • \h*:\h* - 用零个或多个水平空格括起来的冒号
  • (.+) - 第 2 组:线路的其余部分。

你也可以使用parse_ini_string:

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

this PHP demo

preg_replace('/^([^:\v]*):(.*)/m', '$1=\'$2\'', $s) 部分用= 替换每行上的所有第一个: 字符,用单引号将第一个: 之后的部分包裹起来(让parse_ini_string 正确处理额外的=s) ,而parse_ini_string 获取键和值的数组。

【讨论】:

  • 感谢@WiktorStribiżew!出于好奇,您的第一个解决方案是否可以几乎是单行的(我主要是 Python 人,我通常在一行中使用 dict 理解来做这些事情)和 array_filterarray_map
  • @Basj 我不确定第一个解决方案是否是单行的,但parse_ini_string 加上preg_replace 看起来足够短。
  • @Basj PHP 中没有列表/字典理解等价物。
【解决方案2】:

如果保证':'右边的值不包含':'或"\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);

这将打印:

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

【讨论】:

  • 不错的解决方案! :右侧的值可能包含:,例如COMMENT:Try this: print(hello)。在这种情况下,我们可以用 first : 分割吗?
  • 使用您当前的解决方案,使用: 拆分时需要第一和第二部分。我们可以拿第一部分/所有剩余部分吗?即仅与第一个 : 拆分
【解决方案3】:

从您的字符串构建一个流并将其作为 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);

始终使用流,但更“实用”:

$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);

【讨论】:

    猜你喜欢
    • 2019-07-10
    • 1970-01-01
    • 2012-12-20
    • 2013-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-17
    相关资源
    最近更新 更多