将PostgreSQL数组转换为PHP数组

46

我在PHP中读取Postgresql数组时遇到了问题。我尝试过使用explode(),但这会导致包含字符串中逗号的数组断裂,尝试使用str_getcsv()也不行,因为PostgreSQL没有引用日语字符串。

无法正常工作:

explode(',', trim($pgArray['key'], '{}'));
str_getcsv( trim($pgArray['key'], '{}') );

示例:

// print_r() on PostgreSQL returned data: Array ( [strings] => {または, "some string without a comma", "a string, with a comma"} )

// Output: Array ( [0] => または [1] => "some string without a comma" [2] => "a string [3] => with a comma" ) 
explode(',', trim($pgArray['strings'], '{}'));

// Output: Array ( [0] => [1] => some string without a comma [2] => a string, with a comma ) 
print_r(str_getcsv( trim($pgArray['strings'], '{}') ));
11个回答

98
如果您有 PostgreSQL 9.2,您可以这样做:
SELECT array_to_json(pg_array_result) AS new_name FROM tbl1;

结果将以 JSON 数组形式返回

然后在 PHP 方面执行:

$array = json_decode($returned_field);

您还可以进行转换。这里是JSON函数页面。


18

由于这两种解决方案都无法处理多维数组,因此我在此提供适用于任何复杂度的数组的递归解决方案:

function pg_array_parse($s, $start = 0, &$end = null)
{
    if (empty($s) || $s[0] != '{') return null;
    $return = array();
    $string = false;
    $quote='';
    $len = strlen($s);
    $v = '';
    for ($i = $start + 1; $i < $len; $i++) {
        $ch = $s[$i];

        if (!$string && $ch == '}') {
            if ($v !== '' || !empty($return)) {
                $return[] = $v;
            }
            $end = $i;
            break;
        } elseif (!$string && $ch == '{') {
            $v = pg_array_parse($s, $i, $i);
        } elseif (!$string && $ch == ','){
            $return[] = $v;
            $v = '';
        } elseif (!$string && ($ch == '"' || $ch == "'")) {
            $string = true;
            $quote = $ch;
        } elseif ($string && $ch == $quote && $s[$i - 1] == "\\") {
            $v = substr($v, 0, -1) . $ch;
        } elseif ($string && $ch == $quote && $s[$i - 1] != "\\") {
            $string = false;
        } else {
            $v .= $ch;
        }
    }

    return $return;
}

我没有进行过太多测试,但看起来它运行正常。以下是我的测试结果:

var_export(pg_array_parse('{1,2,3,4,5}'));echo "\n";
/*
array (
  0 => '1',
  1 => '2',
  2 => '3',
  3 => '4',
  4 => '5',
)
*/
var_export(pg_array_parse('{{1,2},{3,4},{5}}'));echo "\n";
/*
array (
  0 => 
  array (
    0 => '1',
    1 => '2',
  ),
  1 => 
  array (
    0 => '3',
    1 => '4',
  ),
  2 => 
  array (
    0 => '5',
  ),
)
*/
var_export(pg_array_parse('{dfasdf,"qw,,e{q\"we",\'qrer\'}'));echo "\n";
/*
array (
  0 => 'dfasdf',
  1 => 'qw,,e{q"we',
  2 => 'qrer',
)
*/
var_export(pg_array_parse('{,}'));echo "\n";
/*
array (
  0 => '',
  1 => '',
)
*/
var_export(pg_array_parse('{}'));echo "\n";
/*
array (
)
*/
var_export(pg_array_parse(null));echo "\n";
// NULL
var_export(pg_array_parse(''));echo "\n";
// NULL

P.S.:我知道这是一个非常老的帖子,但我找不到任何适用于版本低于9.2的PostgreSQL的解决方案。


5
可靠的功能将PostgreSQL(一维)数组文字解析为PHP数组,使用正则表达式:

function pg_array_parse($literal)
{
    if ($literal == '') return;
    preg_match_all('/(?<=^\{|,)(([^,"{]*)|\s*"((?:[^"\\\\]|\\\\(?:.|[0-9]+|x[0-9a-f]+))*)"\s*)(,|(?<!^\{)(?=\}$))/i', $literal, $matches, PREG_SET_ORDER);
    $values = [];
    foreach ($matches as $match) {
        $values[] = $match[3] != '' ? stripcslashes($match[3]) : (strtolower($match[2]) == 'null' ? null : $match[2]);
    }
    return $values;
}

print_r(pg_array_parse('{blah,blah blah,123,,"blah \\"\\\\ ,{\100\x40\t\daő\ő",NULL}'));
// Array
// (
//     [0] => blah
//     [1] => blah blah
//     [2] => 123
//     [3] =>
//     [4] => blah "\ ,{@@ daőő
//     [5] =>
// )

var_dump(pg_array_parse('{,}'));
// array(2) {
//   [0] =>
//   string(0) ""
//   [1] =>
//   string(0) ""
// }

print_r(pg_array_parse('{}'));
var_dump(pg_array_parse(null));
var_dump(pg_array_parse(''));
// Array
// (
// )
// NULL
// NULL

print_r(pg_array_parse('{または, "some string without a comma", "a string, with a comma"}'));
// Array
// (
//     [0] => または
//     [1] => some string without a comma
//     [2] => a string, with a comma
// )


4
如果你能预见在该字段中会出现何种文本数据,那么你可以使用array_to_string函数。它在9.1版本中可用。
例如,我确切知道我的数组字段labes永远不会有符号'\n'。因此,我使用函数array_to_string将数组labes转换为字符串。
SELECT 
  ...
  array_to_string( labels, chr(10) ) as labes
FROM
  ...

现在我可以使用PHP函数explode来拆分这个字符串:
$phpLabels = explode( $pgLabes, "\n" );

你可以使用任何字符序列来分隔数组元素。
SQL:
SELECT
  array_to_string( labels, '<--###DELIMITER###-->' ) as labes

PHP:

$phpLabels = explode( '<--###DELIMITER###-->', $pgLabes );

1
在 PHP 中应该这样写:$phpLabels = explode('<--###DELIMITER###-->', $pgLabes); - Paweł Miłosz
感谢@PawełMiłosz。已修正我的回答。 - Nicolai

3

如@Kelt所提到的:

Postgresql数组看起来像这样:{1,2,3,4}

你只需要将第一个 { 和最后一个 } 分别替换为 [ 和 ],然后对其进行json_decode即可。

但他的解决方案仅适用于一维数组。

以下是适用于一维和多维数组的解决方案:

$postgresArray = '{{1,2},{3,4}}';
$phpArray = json_decode(str_replace(['{', '}'], ['[', ']'], $postgresArray));  // [[1,2],[3,4]]

回传:

$phpArray=[[1,2],[3,4]];
$postgresArray=str_replace(['[', ']'], ['{', '}'], json_encode($phpArray));

我没有考虑到多维数组。非常好。 - Kelt

2

根据我创建的线程中的回答,我编写了两个简单的PHP函数可以派上用场:

private function pgArray_decode(string $pgArray){
    return explode(',', trim($pgArray, '{}'));
}

private function pgArray_encode(array $array){
    $jsonArray = json_encode($array, true);
    $jsonArray = str_replace('[','{',$jsonArray);
    $jsonArray = str_replace(']','}',$jsonArray);
    return $jsonArray;
}

1
如果您可以控制查询数据库的方式,为什么不使用unnest()将结果作为行获取,而不是Postgres数组?从那里,您可以本地获取PHP数组。
$result = pg_query('SELECT unnest(myArrayColumn) FROM someTable;');
if ( $result === false ) {
    throw new Exception("Something went wrong.");
}
$array = pg_fetch_all($result);

这样做可以避免尝试自己转换数组字符串表示所带来的开销和维护问题。

1
我尝试了array_to_json的答案,但不幸的是这导致了未知函数错误。在postgres 9.2数据库上使用dbal查询构建器,例如->addSelect('array_agg(a.name) as account_name'),我得到了一个字符串结果,类似于{"name 1","name 2","name 3"}
只有在数组部分包含特殊字符(如空格或标点符号)时才会在周围加上引号。
因此,如果有引号,我将使字符串成为有效的json字符串,然后使用内置的解析json函数。否则,我使用explode。
$data = str_replace(array("\r\n", "\r", "\n"), "", trim($postgresArray,'{}'));
if (strpos($data, '"') === 0) {
    $data = '[' . $data . ']';
    $result = json_decode($data);
} else {
    $result = explode(',', $data);

}


你使用过 PostgreSQL 的 array_to_json() 函数吗? - michaelbn

0

我看到你正在使用 explode(',', trim($pgArray, '{}'));

但是 explode 函数用于将字符串按照指定字符分割(而你却提供了一个数组!!)。应该像这样使用..

$string = "A string, with, commas";
$arr = explode(',', $string);

你想用数组做什么?如果你想要连接,请看一下implode
或者你不确定是否可以指定除逗号以外的分隔符?array_to_string(anyarray, text)

抱歉,我在文章中的代码表述不够清晰。我已经修改了它,使第二个函数参数变成了字符串值而不是数组。需要注意的是,PostgreSQL 的 array_to_string() 对于这种情况并不适用,因为它会移除 NULL 和空值,这使得我无法对数组进行迭代,并将一个数组中的值与另一个数组中对应的值链接起来。 - aardbol
1
为避免 NULL 问题,请将其近似为 PHP ""。使用 array_to_string(anyarray, '","'),即 SELECT '{"'|| array_to_string(anyarray, '","') || '"}' ... 以便 PHP 将其作为字符串 JSON $s 接收,然后使用 $newarray = json_decode($s)。 - Peter Krauss

0

Postgresql 数组的格式如下:{1,2,3,4}

你可以简单地将第一个 { 和最后一个 } 分别替换为 [],然后进行 json_decode。

$x = '{1,2,3,4}';
$y = json_decode('[' . substr($x, 1, -1) . ']');  // [1, 2, 3, 4]

要进行相反的强制转换将是镜像对称的:

$y = [1, 2, 3, 4];
$x = '{' . substr(json_encode($y), 1, -1) . '}';

如果您的数组中有自定义类型,则在建议替换后它不是有效的JSON。例如,{(xyz,123)} 将变成 [(xyz,123)] - dakur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接