insert into table values ('text1;'); insert into table values ('text2')
我该如何使用正则表达式来分解这些查询(获取每个单独的查询)?
我找到了一个非常类似的问题:使用正则表达式查找不在HTML标记中的特定字符串,但它使用的解决方案是特定于.NET的:后向查找(在php中,它会抱怨长度不固定)。
如果有人能给我一些关于如何处理这个问题的提示,我将非常感激。
insert into table values ('text1;'); insert into table values ('text2')
关键是计算你已经通过了多少未转义的引号字符。假设SQL在语法上是正确的,那么在偶数个未转义的引号字符之后的分号将是你想要的,而在奇数个未转义的引号字符之后的分号将是字符串字面量的一部分。(请记住,字符串字面量可以包含正确转义的引号字符。)
如果您想要100%的可靠性,您需要一个真正的SQL解析器,例如this。(我只是在Google上搜索了“PHP中的SQL解析器”。我不知道它是否有效。)
编辑:
我认为仅使用正则表达式无法找到成对的未转义引号字符。也许有些正则表达式专家能够证明我错了,但是在这么多可能的组合中区分转义和未转义的引号字符似乎太难了。我尝试过使用后顾断言和反向引用,但都没有成功。
以下不是纯正则表达式解决方案,但我认为它有效:
preg_match_all("/(?:([^']*'){2})*[^']*;/U", str_replace("\\'", "\0\1\2", $input), $matches);
$output = array_map(function($str){ return str_replace("\0\1\2", "\\'", $str); }, $matches[0]);
基本上,我们暂时用一串极不可能出现的字节替换转义引号字符,在这种情况下是\0\1\2
。之后,所有剩余的引号字符都是未转义的。正则表达式选择由偶数个引号字符前导的分号。然后我们恢复转义的引号字符。(我在那里使用了一个闭包,所以只适用于PHP 5.3。)
如果您不需要处理字符串文字中的引号字符,那么您可以轻松地使用纯正则表达式来完成。
假设SQL语法正确,最好在分号处拆分。
以下正则表达式可以工作,但只有当所有引号成对出现时才有效。
/.+?\'.+?\'.*?;|.+?;/
/.+?[^\\\\]\'.+?[^\\\\]\'.*?;|.+?;/
处理多个单引号对。
/.+?(?:[^\\]\'.+?[^\\]\')+.*?;|.+?;/
已测试以下数据集:
insert into table values ('text1;\' ','2'); insert into table values ('text2'); insert into test3 value ('cookie\'','fly');
返回:
insert into table values ('text1;\' ','2');
insert into table values ('text2');
insert into test3 value ('cookie\'','fly');
我必须承认这是一个相当混乱的正则表达式。它根本无法处理任何SQL语法错误。不过,我很享受设计纯正则表达式的挑战。
正则表达式并不总是擅长处理这种类型的事情。但是以下函数应该可以解决:
function splitQuery($query) {
$open = false;
$buffer = null;
$parts = array();
for($i = 0, $l = strlen($query); $i < $l; $i++) {
if ($query[$i] == ';' && !$open) {
$parts[] = trim($buffer);
$buffer = null;
continue;
}
if ($query[$i] == "'") {
$open = ($open) ? false: true;
}
$buffer .= $query[$i];
}
if ($buffer) $parts[] = trim($buffer);
return $parts;
}
使用方法:
$str = "insert into table values ('text1;'); insert into table values ('text2')";
$str = splitQuery($str);
print_r($str);
输出:
Array
(
[0] => insert into table values ('text1;')
[1] => insert into table values ('text2')
)
你想如何分割?
你可以使用 explode(' ', $query) 将其转换为数组。
或者,如果你想用正则表达式获取 text1 和 text2 的值,你可以使用 preg_match('/(\'([\w]+)\')/', $query, $matches),其中 $matches[1] 是你的值。
preg_match_all('/([\w ]+([\w \';]+))/', $queries, $matches) 将会给你所有符合这个查询模式的匹配结果。