从 SQL SELECT 子句中解析和拆分逗号分隔的字段和表达式

问题描述 投票:0回答:6

我一直在尝试提取字符串内的某些内容。我得到以下字符串:

*, bob, DATE('gdfgfd', 'Fdsfds', ('fdsfdfsd')), george

我想在括号外用逗号分隔,它应该给出:

[
    "*",
    "bob",
    "DATE('gdfgfd', 'Fdsfds', ('fdsfdfsd'))",
    "george"
]

我一直在尝试使用explode,但它甚至在 ( 和 ) ...逻辑内部也被切断了。

所以我这样做了:

[^(,\s]+|\([^)]+\)
,但即使在括号内找到逗号,它也会被剪切。

有人知道该怎么做吗?

编辑:

好的,非常清楚和直接。

我明白了:

SELECT MyField, Field2, Blabla, Function(param), etc FROM table Blabla

我已经得到了字符串

MyField, Field2, Blabla, Function(param), etc
,因为查询是由像
$DB->Select('MyField, Field2, Blabla, Function(param), etc');
这样的多个函数类完成的,但现在我想解析逗号之间的所有内容,所以
MyField, Field2, Blabla, Function(param), etc
变成这样:

  • 我的领域
  • 字段2
  • 布拉布拉
  • 函数(参数)
  • 等等
php sql split sql-parser
6个回答
4
投票

将此作为答案发布,因为它可能比其他任何东西都好:

http://code.google.com/p/php-sql-parser/

使用该项目来解析您的 SQL 语句。结果以数组形式返回,包括

SELECT
FROM
之间的位作为单独的元素,正如您所希望的那样。这比您使用的任何正则表达式解决方案都要好得多。


2
投票

这是我编写的,不支持多字节字符:

编辑:添加字符串感知

<?php


$stack = array();
$stuff = array();

$escaping = false;
$input = "*, bob, [], DATE('g()d\\'f,gfd', ('Fd()sf)ds'), ('fdsfd\"\"()fsd')), ',(),() (,,'";
$len = strlen( $input );
$i = 0;
$curstr = "";
$char;

while( $i < $len ) {
    $char = $input[$i++];

    if( $escaping ) {
        $curstr .= $char;
        $escaping = false;
        continue;
    }

    switch( $char ) {

        case "\\":
            $escaping = true;
            break;

        case '"':
            $top = end( $stack );
            if( $top === '"' ) {
                array_pop( $stack );
            }
            else if( $top !== "'" ){
                $stack[] = '"';
            }

            $curstr .= $char;
            break;

        case "'":
            $top = end( $stack );
            if( $top === "'" ) {
                array_pop( $stack );
            }
            else if( $top !== '"' ) {
                $stack[] = "'";
            }

            $curstr .= $char;           
            break;

        case ",":
            if( count( $stack ) ) {
                $curstr .= $char;
            }
            else {
                $stuff[] = trim($curstr);
                $curstr = "";
            }
            break;

        case "(":
            $top = end( $stack );
            if( $top !== "'" && $top !== '"' ) {
                $stack[] = "(";                   
            }

            $curstr .= $char;
            break;

        case ")":
            $top = end( $stack );

            if( $top !== "'" && $top !== '"' ) {
                if( end($stack) !== "(" ) {
                    die( "Unbalanced parentheses" );
                }
                array_pop( $stack );
            }

            $curstr .= $char;


            break;

        default:
            $curstr .= $char;
            break;

    }
}

if( count( $stack ) ) {
    die( "Unbalanced ".end($stack) );
}

$stuff[] = trim( $curstr );

print_r( $stuff );

/*
    Array
(
    [0] => *
    [1] => bob
    [2] => []
    [3] => DATE('g()d'f,gfd', ('Fd()sf)ds'), ('fdsfd""()fsd'))
    [4] => ',(),() (,,'
)

*/

0
投票

您在评论中表示您准备使用递归,因为您有嵌套列表。但是,正则表达式无法进行递归。这是因为正则表达式无法无限期地“计算”任何内容。由于它无法计算左括号/右括号,因此它无法知道其中有多少层,或者必须走出多少层。

您可以编写极其复杂的正则表达式来处理 N 级深度(参见 anubhava 的答案),但是一旦您遇到具有 N+1 级深度的表达式,您的正则表达式就会失败。这就是为什么我们使用编程语言来解析不规则语言,因为它们可以计算递归(参见diolemo的回答)。 在此递归中,我们可以使用少量的正则表达式。


0
投票

这会起作用(在大多数情况下)。如果引号内有括号(数据的一部分),则会失败。如果需要,您可以扩展代码来处理引号(但随后您必须考虑转义引号和类似的所有内容。正则表达式永远无法正常工作。

编辑:最好使用 SpikeX 回答的 PHP SQL 解析器。

function unreliable_comma_explode($str)
{
   $last_split = 0;
   $len = strlen($str);
   $brackets = 0;
   $parts = array();

   for ($i = 0; $i < $len; $i++)
   {
      if ($str[$i] == '(') 
      {
         $brackets++;
         continue;
      }

      if ($str[$i] == ')')
      {
         if (--$brackets == -1) $brackets = 0;
         continue;
      }

      if ($str[$i] == ',' && $brackets == 0)
      {
         $parts[] = substr($str, $last_split, ($i-$last_split));
         $last_split = $i + 1;
      }
   }

   if (($len-$last_split) > 0)
      $parts[] = substr($str, $last_split, ($len-$last_split));

   return $parts;
}

0
投票

您可以使用这个基于正则表达式的代码来按照您想要的方式获得分割结果:

$str = "*, bob, DATE('gdfgfd', 'Fdsfds', ('fdsfdfsd')), george";
$arr = preg_split('/([^,]*(?:\([^)]*\))[^,]*)+|,/', $str, -1,
                      PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);

更新:

虽然我原来的答案适用于OP发布的示例,但由于一些成员提出的担忧,我发布了一个解决方案,该解决方案也适用于嵌套括号只要括号是平衡的:

$str = "*, bob, DATE('gdfgfd', ('Fdsfds'), ('fdsfdfsd', ('foo'))) 'foo'=[bar]," .
       "john, MY('gdfgfd', ((('Fdsfds'))), ('fdsfdfsd')), george";
$arr = preg_split('/\s*( [^,()]* \( ( [^()]* | (?R) )* \) [^,()]* ) ,?\s* | \s*,\s*/x',
                  $str, -1 , PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
print_r($arr);

输出:

Array
(
    [0] => *
    [1] => bob
    [2] => DATE('gdfgfd', ('Fdsfds'), ('fdsfdfsd', ('foo'))) 'foo'=[bar]
    [3] => john
    [4] => MY('gdfgfd', ((('Fdsfds'))), ('fdsfdfsd'))
    [5] => george
)

注意:尽管这种基于递归的正则表达式模式现在可以与深层嵌套的括号配合使用,但这并不意味着对于某些边缘情况(例如不平衡的括号)不能破坏它。


-1
投票

我不太确定你想在这里做什么..但是如果你只是想提取字符串。你可以只使用内爆。

$array = array("*", "bob", "DATE('gdfgfd', 'Fdsfds', '(\"fdsfdfsd\"))", "george");
echo $test = implode($array, ",");
© www.soinside.com 2019 - 2024. All rights reserved.