正则sqlphp
A. php中正则问题
首先赞扬一下楼主的提问方式:
1. 对问题描述得很清晰;
2. 给出了匹配的范例文本;
3. 指定了正则式所在的语言。
这才应该是规范合理的提问。
言归正传,先根据问题,写出正则,然后落实到PHP语言。如果对分析过程不感兴趣,请直接pagedown看答案。
1. 基础
需要匹配的关键词为:test,你好,程序,分类。
正则表达式:/test|你好|程序|分类/i
(i是表示大小写不敏感模式。如果指定匹配小写的test,则可以去掉i)
2. 限制条件
a. 不在标签内部,例如<img src=asdasda.jpg alt="test" title="你好">,里面虽然有test和你好,但这不满足要求;
b. 本身无链接。例如,<a href="link1" alt="程序设计">程序设计</a>中,>程序设计<中的程序由于处于<a..>程序设计</a>之间,同样不满足要求。
根据以上限制,写出纯粹的正则式(并不能直接用在php中):
(?<!<(?:a|img)[^<>]*)(test|你好|程序|分类)(?![^<>]*</a>)
它表示,(test|你好|程序|分类)的紧临左侧不能出现<a..标签,或者<img标签;紧临右侧不能出现</a>标签。
“紧临”二字至关重要,它将无直接关系的<>都忽略掉,才确保条件的正确执行。怎样实现这一点呢?我使用的是[^<>]*。
正则式的部分这里就交待清楚了。下面将正则式应用到PHP中。
3. php正则式
php使用的正则表达式是PCRE的,它不支持在lookbehind里使用无限量词,即(?<!<(?:a|img)[^<>]*)部分不被PCRE支持。没关系,可以修改之。
(?<!(?:<a|<img))([^<>]*)(test|你好|程序|分类)(?![^<>]*(?:>|</a>))
与2中的正则式相比,前者$1即为所求,其余部分只匹配,不消耗字符;而后者$2为所求,还需要对$1进行处理。
4. php细节
我使用preg_replace_callback函数,以便自定义替换过程。代码如下,随手注释:
<?php
//此处使用heredoc语法,以便支持复杂的长文本。
$x=<<<EOT
请从以下推荐分类中选择合适的分类:<br>电脑/网络 -> <a href="link1" alt="程序设计">程序设计</a><br>
电脑/网络 -> 操作系统/系统故障<br>
<br>
如果没有合适的推荐分类,建议您更改分类,有助于获得准确解答 <br>
<img src=asdasda.jpg alt="test" title="你好"><br>
您还可以输入1381字<br>
test<br>
输入内容已经达到长度限制<br>
EOT;
//关键词数组
$a=array(
1 => "test",
2 => "你好",
3 => "程序" ,
4 => "分类"
);
//记号数组,用于记录哪些匹配是已经替换了的。因为楼主指定“每个词只匹配一次”。
$index=array();
function compute_replacement($groups) {
global $a;
global $index;
if ($index[$groups[2]])
{
//如果该关键词已经被处理过,那么直接返回整个字串($groups[0]),不作处理。
return $groups[0];
}
//查询该关键词在数组中的位置,确定序号,以便生成href="1.html"之类链接地址。
$in=array_search($groups[2],$a);
//将处理过的关键词标记为已处理,避免重复。
$index[$groups[2]]=1;
//返回处理好的文本。
return "$groups[1]<a href=\"$in.html\" title=\"$groups[2]\">$groups[2]</a>";
}
$r= preg_replace_callback('%(?<!<)([^<>]*)(test|你好|程序|分类)(?![^<>]*(?:>|</a>))%s', compute_replacement, $x);
echo $r;
?>
输出的结果为:
请从以下推荐分类中选择合适的<a href="4.html" title="分类">分类</a>:<br>电脑/网络 -> <a href="link1" alt="程序设计">程序设计</a><br>
电脑/网络 -> 操作系统/系统故障<br>
<br>
如果没有合适的推荐分类,建议您更改分类,有助于获得准确解答 <br>
<img src="asdasda.jpg" alt="test" title="你好"><br>
您还可以输入1381字<br>
<a href="1.html" title="test">test</a><br>
输入内容已经达到长度限制<br></body>
注:这里的格式不好,建议去“参考资料”所指向的地址察看更易读版本的解答。
正则表达式论坛:
正则表达式博客:
附:匹配图。高亮部分为匹配结果。
B. 这一段php中正则表达式的详细含义……
拆分这段正则:
'
(and|or)\b.+?(>|<|=|in|like)
\/\*.+?\*\/
<\s*script\b
\bEXEC\b
UNION.+?SELECT
UPDATE.+?SET
INSERT\s+INTO.+?VALUES
(SELECT|DELETE).+?FROM
(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)
你仔细看看,每一条都是对应一种数据库语句可能会出现的情况(除了第3条):
第一条是 单引号,单引号在数据库语句中的作用不用我多说了
第二条是 模糊孙查询
第三条是“*.*”这种数据库万能语句
第四条我觉得可能是<script>的html标签
第五条是 exec数据库动态查询语句
第六条是 UNION联合查询
第七条是 数据库更新语句
第八条是 数据表插入
第九条是 查询或删除数据
第十条是 创建、修改、删除等数据库操作
而这条语句的作用就是使用正则从字符串中查找是否存在数据库操作相关的敏感字符串,从而对该字符串进行一定的处理,保障服务器安全。
(以上)
C. 如何在sql语句中使用正则表达式
sqlserver中,主要有regexp_like,regexp_replace,regexp_substr,regexp_instr四个正则表达式函数。
1、regexp_like:
regexp_like(x,pattern[,match_option]),查看x是否与pattern相匹配,该函数还可以提供一个可选的参数match_option字符串说明默认的匹配选项。match_option的取值如下:
'c' 说明在进行匹配时区分大小写(缺省值);
'i' 说明在进行匹配时不区分大小写;
'n' (.)点号能表示所有单个字符,包括换行(俺还不知道什么地方有用到换行.只知道sql里面可以用chr(10)表示换行、
'm' 字符串存在换行的时候当作多行处理.这样$就可匹配每行的结尾.不然的话$只匹配字符串最后的位置、
示例:
select * from emp where regexp_like(ename,'^a[a-z]*n$');
可以查找ename中以a开头以n结尾的行.例如ename为arwen或arwin或anden.但Arwen不能被匹配.因为默认是区分大小写.如果是
select * from emp where regexp_like(ename,'^a[a-z]*n$','i')
则可以查找ename为Arwen的行记录。
2、regexp_instr:
REGEXP_INSTR(x,pattern[,start[,occurrence[,return_option[, match_option]]]])用于在x中查找pattern。返回pattern在x中出现的位置。匹配位置从1开始。可以参考字符串函数 INSTR(),参数相关:
'start' 开始查找的位置;
'occurrence' 说明应该返回第几次出现pattern的位置;
'return_option' 说明应该返回什么整数。若该参数为0,则说明要返回的整数是x中的一个字符的位置;若该参数为非0的整数,则说明要返回的整数为x中出现在pattern之后 的字符的位置;
'match_option' 修改默认的匹配设置.与regexp_like里面的相同.
示例:
DECLARE
V_RESULT INTEGER ;
BEGIN
SELECT REGEXP_INSTR('hello world','o',1,1,0) INTO V_RESULT
FROM DUAL;
DBMS_OUTPUT.PUT_LINE(V_RESULT);
END;
结果为5,即字母o第一个次出现的位置。
如果regexp_instr('hello world','o',1,1,n)其中n为除0之外的整数。比如1,3。则结果为6.表示第一次出现字母o的后面一个字符的位置。
如果regexp_instr('hello world','o',1,2,0)则结果为9.表示第二次出现字母o的位置.
3、regexp_replace:
REGEXP_REPLACE(x,pattern[,replace_string[,start[,occurrence[, match_option]]]])用于在x中查找pattern,并将其替换为replae_string。可以参考字符串函数 REPLACE(),参数同REGEXP_INSTR函数
示例:
DECLARE
V_RESULT varchar2(90);
BEGIN
SELECT REGEXP_REPLACE('hello world','o','x',1,1) INTO V_RESULT
FROM DUAL;
DBMS_OUTPUT.PUT_LINE(V_RESULT);
END;
结果为hellx world.
如果REGEXP_REPLACE('hello world','o','x'),则结果为hellx wxrld.
如果 REGEXP_REPLACE('hello world','o','x',1,2)则结果为hello wxrld.
4、regexp_substr:
REGEXP_SUBSTR(x,pattern[,start[,occurrence[, match_option]]])用于在x中查找pattern并返回。可以参考字符串函数 SUBSTR(),参数同REGEXP_INSTR函数.
例如:
DECLARE
V_RESULT VARCHAR2(255);
BEGIN
SELECT REGEXP_SUBSTR('hello world','l{2}') INTO V_RESULT
FROM DUAL;
DBMS_OUTPUT.PUT_LINE(V_RESULT);
END ;
结果为ll
查询到匹配的字符串才返回匹配的字符.没查到就返回空。
D. php防止sql注入示例分析和几种常见攻击正则
functioncustomError($errno,$errstr,$errfile,$errline)
{
echo"Errornumber:[$errno],erroronline$errlinein$errfile
";
die();
}
set_error_handler("customError",E_ERROR);
$getfilter="'|(and|or)\b.+?(>|<|=|in|like)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
$postfilter="\b(and|or)\b.{1,6}?(=|>|<|\bin\b|\blike\b)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
$cookiefilter="\b(and|or)\b.{1,6}?(=|>|<|\bin\b|\blike\b)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
functionStopAttack($StrFiltKey,$StrFiltValue,$ArrFiltReq)
{
if(is_array($StrFiltValue))
{
$StrFiltValue=implode($StrFiltValue);
}
if(preg_match("/".$ArrFiltReq."/is",$StrFiltValue)==1&&!isset($_REQUEST['securityToken']))
{
slog("
操作IP:".$_SERVER["REMOTE_ADDR"]."
操作时间:".strftime("%Y-%m-%d%H:%M:%S")."
操作页面:".$_SERVER["PHP_SELF"]."
提交方式:".$_SERVER["REQUEST_METHOD"]."
提交参数:".$StrFiltKey."
提交数据:".$StrFiltValue);
print"resultnotice:Illegaloperation!";
exit();
}
}
foreach($_GETas$key=>$value)
{
StopAttack($key,$value,$getfilter);
}
foreach($_POSTas$key=>$value)
{
StopAttack($key,$value,$postfilter);
}
foreach($_COOKIEas$key=>$value)
{
StopAttack($key,$value,$cookiefilter);
}
functionslog($logs)
{
$toppath="log.htm";
$Ts=fopen($toppath,"a+");
fputs($Ts,$logs." ");
fclose($Ts);
}
?>
E. sql正则表达式常用符号
SQL的查询语句中,有时会需要引进正则表达式为其复杂搜索指定模式。下面给出一些Regexp在
MYSQL语句中应用(非全部):
1) ^
匹配字符串的开始部分。
mysql> SELECT 'fo\nfo' REGEXP '^fo$'; -> 0mysql> SELECT 'fofo' REGEXP '^fo'; -> 12) $
匹配字符串的结束部分。
mysql> SELECT 'fo\no' REGEXP '^fo\no$'; -> 1mysql> SELECT 'fo\no' REGEXP '^fo$'; -> 03) .
匹配任何字符(包括回车和新行)。
mysql> SELECT 'fofo' REGEXP '^f.*$'; -> 1mysql> SELECT 'fo\r\nfo' REGEXP '^f.*$'; -> 14)
[:character_class:]
在括号表达式中(使用[和]),[:character_class:]表示与术语类的所有字符匹配的字符类。标准的类名称是:
alnum
文字数字字符
alpha
文字字符
blank
空白字符
cntrl
控制字符
digit
数字字符
graph
图形字符
lower
小写文字字符
print
图形或空格字符
punct
标点字符
space
空格、制表符、新行、和回车
upper
大写文字字符
xdigit
十六进制数字字符
它们代表在ctype(3)手册页面中定义的字符类。特定地区可能会提供其他类名。字符类不得用作范围的端点。
mysql> SELECT 'justalnums' REGEXP '[[:alnum:]]+'; -> 1
mysql> SELECT '!!' REGEXP '[[:alnum:]]+'; -> 0
5) [[:<:]], [[:>:]]
这些标记表示word边界。它们分别与word的开始和结束匹配。word是一系列字字符,其前面和后面均没有字
字符。字字符是alnum类中的字母数字字符或下划线(_)。
mysql> SELECT 'a word a' REGEXP '[[:<:]]word[[:>:]]'; -> 1mysql> SELECT 'a xword a' REGEXP
'[[:<:]]word[[:>:]]'; -> 0要想在正则表达式中使用特殊字符的文字实例,应在其前面加上2个反斜杠“\”字符。
MySQL解析程序负责解释其中一个,正则表达式库负责解释另一个。例如,要想与包含特殊字符“+”的字符
串“1+2”匹配,在下面的正则表达式中,只有最后一个是正确的:
mysql> SELECT '1+2' REGEXP '1+2'; -> 0mysql> SELECT '1+2' REGEXP '1\+2'; -> 0mysql> SELECT
'1+2' REGEXP '1\\+2'; -> 1 其他的有关Regexp的语法,可直接参考下表:字符 含意
\ 做为转意,即通常在"\"后面的字符不按原来意义解释,如/b/匹配字符"b",当b前面加了反斜杆后/\b/,转意
为匹配一个单词的边界。
-或-
对正则表达式功能字符的还原,如"*"匹配它前面元字符0次或多次,/a*/将匹配a,aa,aaa,加了"\"后,/a\*/将
只匹配"a*"。
^ 匹配一个输入或一行的开头,/^a/匹配"an A",而不匹配"An a"
$ 匹配一个输入或一行的结尾,/a$/匹配"An a",而不匹配"an A"
* 匹配前面元字符0次或多次,/ba*/将匹配b,ba,baa,baaa
+ 匹配前面元字符1次或多次,/ba*/将匹配ba,baa,baaa
? 匹配前面元字符0次或1次,/ba*/将匹配b,ba
(x) 匹配x保存x在名为$1...$9的变量中
x|y 匹配x或y
{n} 精确匹配n次
{n,} 匹配n次以上
{n,m} 匹配n-m次
[xyz] 字符集(character set),匹配这个集合中的任一一个字符(或元字符)
[^xyz] 不匹配这个集合中的任何一个字符
[\b] 匹配一个退格符
\b 匹配一个单词的边界
\B 匹配一个单词的非边界
\cX 这儿,X是一个控制符,/\cM/匹配Ctrl-M
\d 匹配一个字数字符,/\d/ = /[0-9]/
\D 匹配一个非字数字符,/\D/ = /[^0-9]/
\n 匹配一个换行符
\r 匹配一个回车符
\s 匹配一个空白字符,包括\n,\r,\f,\t,\v等
\S 匹配一个非空白字符,等于/[^\n\f\r\t\v]/
\t 匹配一个制表符
\v 匹配一个重直制表符
\w 匹配一个可以组成单词的字符(alphanumeric,这是我的意译,含数字),包括下划线,如[\w]匹配
"$5.98"中的5,等于[a-zA-Z0-9]
\W 匹配一个不可以组成单词的字符,如[\W]匹配"$5.98"中的$,等于[^a-zA-Z0-9]。