逗号在括号外面将字符串分割

3
我有一个字符串,我想按逗号分割它,但只有在逗号不嵌套在括号内时才能分割。这是一个相当常见的用例,我一直在阅读论坛中已经解答的帖子,但并没有找到我正在寻找的内容。
具体来说:我有一个字符串(SQL SELECT ... FROM 语句),我想从中提取由逗号分隔的列表元素(即要从中选择的列名)。然而,这些元素可能包含括号,并且有效地成为函数调用。例如,在 SQL 中可以执行以下操作
SELECT TO_CHAR(min(shippings.shippingdate), 'YYYY-MM-DD') as shippingdate, nameoftheguy FROM shippings WHERE ...

显然,我现在希望有一个数组,其中第一个元素为...
TO_CHAR(min(shippings.shippingdate), 'YYYY-MM-DD') as shippingdate

作为第二个元素

nameoftheguy

到目前为止我采用的方法是PHP和正则表达式:按不在方括号内且有嵌套方括号的逗号(,)分割字符串PHP:按逗号(,)分割字符串,但忽略方括号内的内容?除括号外其他地方分割字符串?以及PHP:按逗号分割字符串,但当在大括号或引号之间时不分割?(着重于其中的正则表达式,因为我想用单个正则表达式实现),但在我的测试区域中,它们都没有给出正确的结果。实际上,它们要么什么也没分割,要么分割过头了。
$Input: SELECT first, second, to_char(my,big,house) as bigly, export(mastermind and others) as aloah FROM
$Output: Array ( [0] => first [1] => second [2] => to_char [3] => (my,big,house) [4] => as [5] => bigly [6] => export [7] => (mastermind and others) [8] => as [9] => aloah )

我的测试区代码如下

<?php
function test($sql){
    $foo = preg_match("/SELECT(.*?)FROM/", $sql, $match);
    $bar = preg_match_all("/(?:[^(|]|\([^)]*\))+/", $match[1], $list);
    //$bar = preg_match_all("/\((?:[^()]|(?R))+\)|'[^']*'|[^(),\s]+/", $match[1], $list);
    //$bar = preg_match_all("/[,]+(?![^\[]*\])/", $match[1], $list);
    //$bar = preg_match_all("/(?:[^(|]|\([^)]*\))+/", $match[1], $list);
    //$bar = preg_match_all("/[^(,\s]+|\([^)]+\)/", $match[1], $list);
    //$bar = preg_match_all("/([(].*?[)])|(\w)+/", $match[1], $list);
    print "<br/>";
    return $list[0];
}

print_r(test("SELECT first, second, to_char(my,big,house) as bigly, export(mastermind and others) as aloah FROM"));
?>

我并不是一个正则表达式专家,但如果可能的话,我想在一行中完成这个拆分。


1
你真的应该考虑使用一个合适的解析器。如果你收到一个包含子查询的查询,例如 SELECT x, (SELECT y FROM z) AS p, z FROM ...,那么你会怎么做呢?在这种情况下,你的 $foo = preg_match("/SELECT(.*?)FROM/", $sql, $match); 代码将无法正常工作... - Nick
假设所有的括号都是平衡的,你可以使用类似于 ,\s*(?![^(]*\)) 的方式来进行分割。 - PJProudhon
好的。根据您刚才的解释,我可能不应该像现在这样使用正则表达式来处理第一行。但是对于第二行(有疑问的那一行),如果按照我想象中的方式得到答案,它仍然应该返回正确的列表,即["x", "(SELECT y FROM z) AS p", "z"]。 - conni
@PJProudhon,我得到了一个错误:“警告:preg_match_all():未找到结束定界符“,” - conni
1个回答

0

根据这里的讨论,我写了一个解析器来解决这个问题。它相当丑陋,但它完成了工作(至少在某些限制内)。为了完整性(如果其他人可能遇到同样的问题),我在这里发布它:

function full($sqlu){
    $sqlu = strtoupper($sqlu);
    if(strpos($sqlu, "SELECT ")===false || strpos($sqlu, " FROM ")===false) return NULL;
    $def      = substr($sqlu, strpos($sqlu, "SELECT ")+7, strrpos($sqlu, " FROM ")-7);
    $raw      = explode(",", $def);
    $elements = array();
    $rem      = array();
    foreach($raw as $elm){
        array_push($rem, $elm);
        $txt = implode(",", $rem);
        if(substr_count($txt, "(") - substr_count($txt, ")") == 0){
            array_push($elements, $txt);
            $rem = array();
        }
    }
    return $elements;
}

当输入以下字符串时

SELECT first, second, to_char(my,(big, and, fancy),house) as bigly, (SELECT myVar,foo from z) as super, export(mastermind and others) as aloah FROM table

它返回

Array ( [0] => first [1] => second [2] => to_char(my,(big, and, fancy),house) as bigly [3] => (SELECT myVar,foo from z) as super [4] => export(mastermind and others) as aloah ) 

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