当用户在自由格式字段中使用分号时,我的系统会导致错误。我已将其描述为一个简单的爆炸声明:
$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
所以它没有注意到分号在引号内。 (据我所知,系统中不同位置的引用字符串总是用单引号,但有时它们可能是双引号,我不确定。)
谁能告诉我怎么做?我怀疑我能用一个非常复杂的正则表达式做到这一点,但这是我的头脑。
答案 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。
参考