在分号上的PHP中拆分sql语句(但不在引号内)

时间:2014-06-26 05:50:54

标签: php regex split quotes

当用户在自由格式字段中使用分号时,我的系统会导致错误。我已将其描述为一个简单的爆炸声明:

$array = explode( ";", $sql );

因为这条线位于从整个系统调用的子程序中,所以我想用一些能够正确拆分的东西替换这一行,而不会破坏系统的其余部分。我以为我是str_getcsv的赢家,但这也不够复杂。请看以下示例

$sql = "BEGIN;INSERT INTO TABLE_A (a, b, c) VALUES('42', '12', '\'ab\'c; DEF');INSERT INTO TABLE_B (d, e, f) VALUES('42', '43', 'XY\'s Z ;uvw') ON DUPLICATE KEY UPDATE f='XY\'s Z ;uvw';COMMIT;";

$array = str_getcsv($sql, ";", "'");
foreach( $array as $value ) {
    echo $value . "<br><br>";
}

当我运行时,输出以下内容:

  

BEGIN

     

INSERT INTO TABLE_A(a,b,c)VALUES(&#39; 42&#39;,&#39; 12&#39;,&#39; \&#39; ab \&#c; c < / p>      

DEF&#39)

     

INSERT INTO TABLE_B(d,e,f)VALUES(&#39; 42&#39;,&#39; 43&#39;,&#39; XY \&#Z;

     

uvw&#39;)关于重复键更新f =&#39; XY \&#39; s

     

UVW&#39;

     

COMMIT

所以它没有注意到分号在引号内。 (据我所知,系统中不同位置的引用字符串总是用单引号,但有时它们可​​能是双引号,我不确定。)

谁能告诉我怎么做?我怀疑我能用一个非常复杂的正则表达式做到这一点,但这是我的头脑。

1 个答案:

答案 0 :(得分:6)

(*跳过)(*失败)魔术

live PHP demo显示以下两个选项的输出(带或不带分号)。

这就是你需要的:

$splits = preg_split('~\([^)]*\)(*SKIP)(*F)|;~', $sql);

See demo看到我们正在分裂正确的分号。

<强>输出:

[0] => BEGIN
[1] => INSERT INTO TABLE_A (a, b, c) VALUES('42', '12', '\'ab\'c; DEF')
[2] => INSERT INTO TABLE_B (d, e, f) VALUES('42', '43', 'XY\'s Z ;uvw')
[3] => COMMIT
[4] =>

空白项目#4是最终;另一侧的匹配项。另一种选择是保留分号(见下文)。

选项2:保持半冒号

如果你想保留分号,请选择:

$splits = preg_split('~\([^)]*\)(*SKIP)(*F)|(?<=;)(?![ ]*$)~', $sql);

<强>输出:

[0] => BEGIN;
[1] => INSERT INTO TABLE_A (a, b, c) VALUES('42', '12', '\'ab\'c; DEF');
[2] => INSERT INTO TABLE_B (d, e, f) VALUES('42', '43', 'XY\'s Z ;uvw');
[3] => COMMIT;

<强>解释

此问题是此问题中向"regex-match a pattern, excluding..."

解释的技术的典型案例

在交替|的左侧,正则表达式\([^)]*\)匹配完成(parentheses)然后故意失败,之后引擎跳转到字符串中的下一个位置。右侧与您想要的;字匹配,我们知道它们是正确的,因为它们与左侧的表达式不匹配。现在拆分它是安全的。

在选项2中,我们保留分号,我们右边的匹配匹配一个位置,但没有字符。这个位置由lookbehind (?<=;)断言,它声明一个;紧接在该位置之前,而负向前瞻(?![ ]*$)断言后面的内容不是可选空格然后结束字符串(所以我们避免最后一个空匹配)。

示例代码

请检查live PHP demo

参考