《正则表达式》PPT共享(公司内部培训)

近来我越发懒惰,博客很少更新,最近在公司内部做了一个培训,关于正则表达式的,发到博客里来凑数吧,也算分享给其他朋友了。至于正则表达式的重要性,想必不用我说了吧。

发明BSD、TCP/IP、csh、vi和NFS的SUN首席科学家Bill Joy说过,在计算机体系结构领域里,缓存是唯一称得上伟大的思想的。其他的一切发明和技术不过是在不同场景下应用这一思想而已。在计算机软件领域里,情形也大体相似。如果罗列这个领域的伟大发明,绝对不超过二十项。这些包括分组交换网络、WEB、lisp、哈希算法、UNIX、编译技术、关系模型、面向对象、XML这些大名鼎鼎的家伙,而正则表达式绝对不应该被漏掉。

你还记得大学时候的课程编译原理中提到的 NFA\DFA 吗?还有印象吗?你知道PHP编译器扫描你写的PHP代码字符串的时候,是如何区分哪个是变量,哪个是常量的吗?你知道SQL语法分析器是如何判断你的WHERE条件中出现的SELECT\FROM\ WHERE等关键字是字符串,而不是子查询等关键字的?
你知道.ini配置文件跟xml配置文件对程序来说,是如何扫描解析的吗?他们一样吗?如果你想知道,那你务必的熟悉正则表达式,精通正则表达式。不光能写出正则实现功能,还得能写出高效的正则表达式。(编译原理的课程里,有关于NFA\DNA的大篇幅描述,想写出自己的编程语言,正则是躲不掉的。)

不扯淡了,附PPT吧。
(PPT模版是网上网友的,我是借用他的,其中有几张PPT是直接用他的。很好区分,动画效果很炫的(主要是文字层下的颜色,大约10页吧,简单描述那几页)就是原作者的,其他都是我自己写的。作者信息在PPT最后一页有)
PDF也放出来,效果没PPTX好,很多层都被遮住了。

猛击这里下载《正则表达式》PPTX

或者您也可以在线阅读:

正则表达式与数学(方程式、线性方程)

大清早的打QQ去,收到一位网友的信息。问得是正则表达式判断素数的。去年看到过,没记录下来。
正则表达式如下:

^1?$|^(11+?)\1+$ 可以判断素数(换成n个1的形式,n为数字的大小。比如5转换为11111;3转换为111;2转换为11。)

什么是素数?
初中学的吧。我们老师当初教我们的是“质数”。看下概念:
质数又称素数。指在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数。
换句话说,只有两个正因数(1和自己)的自然数即为素数。比1大但不是素数的数称为合数。1和0既非素数也非合数。

这个正则表达式是什么意思?
【^1?$|^(11+?)\1+$】中间用【|】分开。【|】在正则语法里,表示“或”,作用于其前后两个单元。(还是不明白的看下面,明白的跳过下面这段)

比如【ab|cd】可以匹配“ab”、也可以匹配“cd”,意思是除了“ab”就是“bc”,如果想匹配“abd”、“acd”那【|】的作用域得改下,加个范围
改成【a(b|c)】(匹配结果分配组)或者【a(?:b|c)d】(匹配结果不分配组,更高效率)。

继续刚刚的正则,分为两个分支,其一为【^1?$】和【^(11+?)\1+$】。其中【^】脱字符在正则语法中,除了在中括号【[]】中都是代表开头的意思,在中括号中的表示非。
第一个分支【^1?$】匹配的是“1”或者“”(空字符串)。
第二个分支【^(11+?)\1+$】,先看下括号内的【(11+?)】匹配的是字符“1”后面接着【1+】就是1到无数个1。后面的【?】问号表示非贪婪,就是尽量少的匹配。
接着往后看【\1+】中,【\1】表示引用已匹配的第一个组的结果。也就是第一个【()】括号匹配的结果。同理【\2】就是第二个括号捕获的结果。(小提示:上面提到的【(?:)写法就是不分配组,这样引用的话,就引用不到了】)
【+】就是1到无数个了。这个表达式我们可以这么看。【(11+?)】看成数学中的1+n,其中n为大于0的正整数。外面的【\1+】也就是引用前面这个组的次数。理解成m倍,其中m为大于0的正整数。
那整个表达式就是(1+n)*m。因为n、m都大于0,那么1+n肯定大于1,最小为2,最大为无穷大;m最小为1,最大为无穷大。
那么,一个大于2的正整数的任何大于零的倍数永远都是合数,也就是非素数。

再回过头来看看这个表达式。匹配的分别为0个或1个字符串“1”,也就是数字0,数字1。和其他所有合数。整个表达式,如果成功匹配就是非素数,如果不匹配就是质数。这就是对的了。

if (preg_match('/^1?$|^(11+?)\1+$/i', $subject)) {
	#不是素数
} else {
	# 是素数
}

小提示:此鉴定是否为素数方法仅研究学习用,不能用到正式程序中,字符串过长,会造成非常恐惧大的回溯

英文博客地址:http://blog.stevenlevithan.com/archives/algebra-with-regexes

在上面的博文中,有提到两个方程式与正则表达式,我们一起来研究下。

  • 二元方程17x + 12y = 51,其表达式【^(.*)\1{16}(.*)\2{11}$】。很好理解。【(.*)】也就是0到无数个【.】点号。(这里是接着上文说的,其实,【.】点号想表示的是字符“1”)
    也就是0到无数个1,后面【\1】引用一次。后面【{16}】就是16次。作用于前面的【\1】,也就是16次引用。加上开始的【(.*)】一共正好17次。后面一个就不说了,跟这个一样。
    正则引擎会依次尝试【(.*)】中0到无数个字符“1”,0个字符“1”,1个字符“1”,2个字符“1”一直增加的尝试。直到成功,否则要尝试完所有字符“1”的最大个数(这里是51个字符“1”)。
  • 二、三元方程式11x + 2y + 5z = 115,其表达式为【^(.*)\1{10}(.*)\2{1}(.*)\3{4}$】,理解就跟上面那个一样。注意【\2】、【\3】值得是第2,第3个括号捕获的内容,别看花眼了。

——————-分割线——————
上面几个有意思的数学题都是将整数转换为对应个数的字符“1”。下面这个,是转换为二进制数的。
先吃饭,以后再写。

关于网上流传查找PHP webshell的python脚本中,不严谨的代码

不是闲着蛋疼,也不是批评谁,只是不忍心看到不严谨的代码在网上被疯传,误导初学者。以下引用的代码以及思路来自网络,只针对代码,不针对人。如有雷同,纯属巧合。

来自某大牛python版本的检测服务器上PHP代码中webshell的脚本代码片段如下:

findtype=['.php','.inc'] #要检查的文件后缀类型
keywords=[ ["eval\(\$\_POST","发现PHP一句话木马!"], ["(system|shell_exec|exec|popen)","发现PHP命令执行函数!"]]

然后,接着是python处理上面列表(数组),把每个列表的第一个元素作为正则表达式内容,然后进行匹配处理的。先看第一个检测“PHP一句话木马”的这个正则。【eval\(\$\_POST】这里转义了字符【_】,字符“_”不是元字符,不需要转义的,其实,转义也无妨,聪明的正则表达式引擎会识别修正的。来说下这个思路的遗漏之处。这里仅仅匹配字符串“eval(”后面的变量“$_POST”,如果是“$_GET”呢?如果是“$_REQUEST”呢?如果程序先把$_POST/$_GET/$REQUEST的key的变量先赋值给一个自定义的变量名,然后在用PHP的“eval”函数执行这个变量呢?是不是就查找不到了?其实,并不需要知道“eval”函数执行的变量名是什么,只要查找“eval(”即可,当然,还要注意“eval”函数后面可以有空格,tab键之类空白字符。上面的代码片段中,也没考虑到这一点。

某检测PHP webshell的python脚本考虑欠佳。

再看看下一个列表的第一个元素。【(system|shell_exec|exec|popen)】,这个正则的意思是只要字符串里包含“system”、“shell_exec”、“exec”、“popen”这四组字符串即判定为危险字符。很明显,这个方法太不严谨。如果程序员写的代码中,包含了这四组字符,即可被判定为危险函数。很不准确,误报率极高。见下图

某检测PHP webshell的python脚本考虑欠佳。

到底什么样的代码是可疑的代码?关键词是什么?

可疑的代码肯定是由可以执行危险操作的函数构成,可以执行危险操作的PHP函数最重要的就是“eval”函数了,对于加密的PHP代码(仅变形字符串,非zend等方式加密),肯定要用到“eval”函数,所以,对于不管是用哪种加密方法的代码,肯定要用到“eval”函数。其次就是可以执行系统命令的函数了,比如上面某牛的代码中提到的四个“system”、“shell_exec”、“exec”、“popen”。当然还有其他的,比如passthru等。PHP还支持“·”字符(ESC键下面那个)直接执行系统命令。我们可以把正则写成这样【\b(?P<function>eval|proc_open|popen|shell_exec|exec|passthru|system)\b\s*\(】。

检测PHP webshell的python脚本相对较为严谨的匹配

解释一下:

大家都知道【\b\b】用来匹配单词两边的位置的。要保证【\b\b】中间的是单词,即使函数名前面加特殊字符,也一样通过匹配,比如加@来屏蔽错误。后面的【\s*】用来匹配空白字符的,包括空格,tab键,次数为0到无数次。前面的【(?P)】是捕获命名组。用来当作python代码直接引用匹配结果的key。

还有的网友提到了,如果我把代码放到图片拓展名的文件里呢?那你只检测.php,.inc的文件,还是找不到我的呀。嗯,是的,如果恶意代码在gif、jpg、png、aaa等乱七八糟的拓展名文件里,是不能被apache、IIS等web Services解析的,必须通过include/require(_once)来引入。那么,我们只要匹配include/require(_once)后面的文件名是不是常规的“.php”、“.inc”文件。如果不是,则为可疑文件。正则如下【(?P<function>\b(?:include|require)(?:_once)?\b)\s*\(?\s*["'](?P<filename>.*?(?<!\.(?:php|inc)))["']】。

检测PHP WEBSHELL的python脚本较为严谨做法

解释一下:

先看【(?P<function>\b(?:include|require)(?:_once)?\b)】,【(?P<name>)】为正则表达式的“命名捕获”,PHP中有同样的用法。也就是说,在这括号内的捕获的数据,会分配到结果数组的key为“name”的value中。再看里面的【\b(?:include|require)(?:_once)?\b】,【\b\b】不解释了,为单词边界位置。里面的【(?:include|require)】匹配字符串“include”、“require”两个单词,其中前面的【(?:)】未不分配组,用于提高效率,可以去掉【?:】变成【(include|require)】。在后面一个【(?:_once)】也是做不分配组的操作,便于提高正则表达式效率。同样,后面的量词是“?”代表这个组可有可无。就满足了“include”、“include_once”、“require”、“require_once”四种情况。有的朋友可能这样写【(include|include_once|require|require_once)】也能实现目的。但是,为了更搞的效率,我们对这个正则做优化,针对部分字符串做分支更改,改成上面那个【\b(?:include|require)(?:_once)?\b】。

再看下面的【\s*\(?\s*["'](?P<filename>.+?(?<!\.(?:php|inc)))["']】中,【\s*】匹配空白字符,包括空格,tab键等。后面的【\(?】,匹配字符“(”,后面的量词“?”表示这半个小酷括号可有可无。防止“incude “123.php””这种没有括号的情况。再后面【["']】匹配双引号,单引号的。最后的也是。再看看这个【(?P<filename>.+?(?<!\.(?:php|inc)))】,其中【(?P<filename>)】上面介绍了,为命名捕获,把结果放到match.group(“filename”)里。【.*?】为任意字符,后面的量词是“忽略优先量词”,也就是平常说的“非贪婪”。这里最少匹配零个,(防止.aa、.htaccess这种没有文件名,只有文件拓展名的文件被引入)。后面的【(?<!\.(?:php|inc))】,这里用到了反向零宽断言(环视)的非操作(只匹配位置,不匹配字符串,跟【^$\b】等一样)。这个表达式是针对这个位置的后面字符起作用的,也就是说后面的【["']】的前面不能是“.php”、“.inc”,这里也就是取了文件名的最后的拓展名。(正则里,可以用【^】对字符取非,但是不能对“字符串组”取非,这里用了零宽断言来实现。)

综上所述,最后,鄙人给出的python代码如下:

#!/usr/bin/python
#-*- encoding:UTF-8 -*-
###
## @package
##
## @author      CFC4N   <cfc4nphp@gmail.com>
## @copyright   copyright (c) Www.cnxct.Com
## @Version     $Id: check_php_shell.py 37 2010-07-22 09:56:28Z cfc4n $
###
import os
import sys
import re
import time
def listdir(dirs,liston='0'):
	flog = open(os.getcwd()+"/check_php_shell.log","a+")
	if not os.path.isdir(dirs):
		print "directory %s is not exist"% (dirs)
		return
	lists = os.listdir(dirs)
	for list in lists:
		filepath = os.path.join(dirs,list)
		if os.path.isdir(filepath):
			if liston == '1':
				listdir(filepath,'1')
		elif os.path.isfile(filepath):
			filename = os.path.basename(filepath)
			if re.search(r"\.(?:php|inc|html?)$", filename, re.IGNORECASE):
				i = 0
				iname = 0
				f = open(filepath)
				while f:
					file_contents = f.readline()
					if not file_contents:
						break
					i += 1
					match = re.search(r'''(?P<function>\b(?:include|require)(?:_once)?\b)\s*\(?\s*["'](?P<filename>.*?(?<!\.(?:php|inc)))["']''', file_contents, re.IGNORECASE| re.MULTILINE)
					if match:
						function = match.group("function")
						filename = match.group("filename")
						if iname == 0:
							info = '\n[%s] :\n'% (filepath)
						else:
							info = ''
						info += '\t|-- [%s] - [%s]  line [%d] \n'% (function,filename,i)
						flog.write(info)
						print info
						iname += 1
					match = re.search(r'\b(?P<function>eval|proc_open|popen|shell_exec|exec|passthru|system)\b\s*\(', file_contents, re.IGNORECASE| re.MULTILINE)
					if match:
						function = match.group("function")
						if iname == 0:
							info = '\n[%s] :\n'% (filepath)
						else:
							info = ''
						info += '\t|-- [%s]  line [%d] \n'% (function,i)
						flog.write(info)
						print info
						iname += 1
				f.close()
	flog.close()
if '__main__' == __name__:
	argvnum = len(sys.argv)
	liston = '0'
	if argvnum == 1:
		action = os.path.basename(sys.argv[0])
		print "Command is like:\n	%s D:\wwwroot\ \n	%s D:\wwwroot\ 1	-- recurse subfolders"% (action,action)
		quit()
	elif argvnum == 2:
		path = os.path.realpath(sys.argv[1])
		listdir(path,liston)
	else:
		liston = sys.argv[2]
		path = os.path.realpath(sys.argv[1])
		listdir(path,liston)
	flog = open(os.getcwd()+"/check_php_shell.log","a+")
	ISOTIMEFORMAT='%Y-%m-%d %X'
	now_time = time.strftime(ISOTIMEFORMAT,time.localtime())
	flog.write("\n----------------------%s checked ---------------------\n"% (now_time))
	flog.close()
## 最新代码在文章结尾的链接里给出了。2010/07/31 更新。

仅供参考,欢迎斧正。

下面截图为扫描Discuz7.2的效果图,当然,也有误报。相对网上流传的python脚本,误报更少,更精确了。

检测PHP WEBSHELL的python脚本的检测结果

问:这个方法完美了吗?可以查找目前已知的所有危险函数文件了吗?
答:不能,如果include等引入的文件没有拓展名,这里就匹配不到了。
问:如何解决?
答:留给你解决,聪明的你,肯定可以搞定。
PS:“`”反引号 执行命令的还没写,暂时没好的办法。容易跟SQL语句中的反引号混淆。不太好匹配。如果光匹配反引号就提示的话,那误报太大了。待定吧。(术业有专攻,请勿因为一处不好的代码,否定一个人的能力。你懂的。再次重申,此文只针对代码,不针对人。其次,鄙人给出的python代码随便复制,随便传播,爱留版权就留版权,不爱留就删了相关字符,也就是您爱干吗干吗。)
我先休息一会,明天再说。(前半句为三国杀曹仁的台词,哈。)

=============================我是万恶的分割线======================================
最新代码在这里给出了。

snoopy.class.php中_striplink方法的正则分析

上周提到老爷机坏了,昨天早上早早的抱去修理,维修工程师更换硬件排除法最后得知,故障出现在显卡,插入显卡,主板灯亮,按开关,没反映,或者CPU风扇抖动一下即停止。卸下显卡,开机正常。这位维修人员的结论是显卡坏了,建议更换显卡。我们有N卡9200,只要320元。我提出把显卡放到别的主机上试试,被拒绝了,好吧,那我也拒绝。最后,对方收我20元检测费。虽然我心里有点不情愿,他只是换了几个零件的简单排除法得出很有可能的结论而已,并没有告诉我准确的病因,是显卡的哪里出问题了?能否维修?他都没有告诉我。尽管这样,我还是给了,也没争论。

主机抱回来之后,自己卸下了显卡。开机,上网,打算到网上买块新的。老的显卡是我07年在学校买的。ATI RADEON X550,在当初,还算比较前卫的硬件。周五还赞扬其能勤奋工作到现在,而结果得知出问题的就是它。工作了3年多,不到四年,还能接收吧。主板换了华硕P5GC-MX/1333 ,作为硬件小白的我,也不知道换什么显卡较好。熟悉硬件的朋友,麻烦留言告诉一下,感谢了。现在打开网页,拉动浏览器滑动条的时候,卡的比较厉害,播放电影更是,像放映幻灯片一样。实在不能接受。遂网上搜了相关信息。价格在500左右。是有点贵了。遂暂时作罢。一整天心情都很低落,或者说,自从老爷机出问题了,心情一直很差。中午窝在家里,洗了洗衣服,趁太阳好,把冬天穿的衣服拿出来晒了晒。又挪动电脑附近的两个大桌子,打扫卫生,清理一番。一直忙到下午3点,头饿的晕忽忽的。匆忙到沙县小吃随便吃了点,下午,看会书,睡了会觉,早上送GF回家,起得太早了,困的厉害。睡醒之后,都7点多了,照照镜子,看看自己蓬松的发型,猥琐的样子,不禁笑了一笑。洗了把脸,到小区的理发店里剪了头发,由于是小区内,专门给老头老太太理发的,所以费用仅5元,在上海,应该没有比这还低了吧。晚上,把显示器接到电视盒上,看了会电视,差不多都是娱乐类的弱智脑残节目,我极为反感觉,又将显示器接回主机箱,开机到当当网上看了一番。因工作需要,要做个代码自动审计化的程序,我的想法是,不仅仅停留在对关键危险函数的正则匹配,而是做到语法分析,识别未初始化的变量、未过滤的富文本字段、SQL注入的方法等。想必,肯定的熟悉《编译原理》以及LEX、YACC之类相关知识,后悔大学没认真听,造成自己基础薄太弱了。当然,我也觉得大学的时候,直接教学生这么底层的知识没有循序渐进的引导学生去深入更好。可以从最简单的网页制作开始,然后,让学生对其感兴趣,再引导到动态,然后,联合数据库,然后效率优化,然后代码执行原理,然后编译原理类似这种步骤引导学生更好。比起现在的方式,可能更让学生感兴趣。(扯远了)东看西看,忙到了12点多才睡。

对于已经习惯8点左右起床上班的我来说,生物钟在早上8点左右,准时醒,有时候想赖床,但也无法再次入睡。起床洗漱,打开电脑,玩了两局web的三国杀,逛逛论坛,觉得饿了,才去煮点面条吃。回来继续,无意中打开PPC,看到一位网友“落叶人生”同学的帖子问一个正则问题,纠结了一下是否解释一下,以便给对方释疑解惑,也加固自己的知识。犹豫半天,觉得还是写下吧。下面正题:

PPC链接http://bbs.phpchina.com/thread-189797-1-1.html

  1. preg_match_all(“‘<\s*a\s.*?href\s*=\s*([\"\'])?(?(1)(.*?)\\1|([^\s\>]+))[^>]*>?(.*?)</a>’isx”,$document,$links);

对于一般的规范链 接能很好的使用,但不知道为什么对href=后面的网址不含引号的情况无法提取成功,哪个朋友帮分析下?谢谢!

其实,我没明白他的意思是想提取链接地址href后面的内容的,还是提取整个标签a中间的所有。

在下面的回帖中,他提到了这个正则来自snoopy.class.php的_striplink方法中的正则,遂到sf.net上下载一份原版到本地。

代码如下

	function _striplinks($document)
	{
		preg_match_all("'<\s*a\s.*?href\s*=\s*			# find <a href=
						([\"\'])?					# find single or double quote
						(?(1) (.*?)\\1 | ([^\s\>]+))		# if quote found, match up to next matching
													# quote, otherwise match up to next space
						'isx",$document,$links);

		// catenate the non-empty matches from the conditional subpattern

		while(list($key,$val) = each($links[2]))
		{
			if(!empty($val))
				$match[] = $val;
		}

		while(list($key,$val) = each($links[3]))
		{
			if(!empty($val))
				$match[] = $val;
		}

		// return the links
		return $match;
	}

先看下修饰符部分。【isx】,大家都知道【i】是不区分大小写;【s】是点号通配模式,也就是元字符【.】可以匹配换行符。在这里的作用是为了防止链接中出现换行的情况;
【x】宽松排列和注释模式,也就是空白字符不会被当成正则表达式的一部分,#后面的为注释,方便阅读的作用了。顺便说下括号后面的双引号后面的单引号,跟修饰符i前面的单引号,他们只是正则的起始标识,不是正则的一部分。其实,表达式里有单引号了,这里最好使用别的,比如斜杠,或者闭合的大括号等。
再看表达式【<\s*a\s.*?href\s*=\s*】,其后面也注释了,是匹配“<a href=”的,第一个【\s】匹配”<”到”a”之间的空白字符,不过一般都没的吧。都直接写”<a……”。后面的【\s】也是空白字符,不详细解释了。在后面的【.*?】是匹配“<a “到“href=”之间的内容,比如”title=”…..”、target=”….”等。【href=】中没有元字符,就是匹配【href=】.

再后面【([\"\'])?】这里的单引号,双引号是被PHP语法转义的,真正的表达式是【(["'])?】,匹配单引号和双音号,并且,用小括号分配了组(整个表达式的第一组),后面有【?】量词,意味这可有可无。

再后面很重要了。去掉PHP的转义字符之后【(?(1) (.*?)\1 | ([^\s>]+)】,这是一个正则表达式的高级用法,叫“条件判断”表达式。语法为【(? if then| else)】。这里的if部分为【(1)】也就是前面说的“整个表达式的第一组”的内容,如果为真,则使用表达式【(.*?)\1】来匹配(\1是引用捕获的第一个组的内容),如果【(1)】没匹配到,则使用【([^\s>]+)】来匹配。这样,就可以实现没有引号的情况下,也是可以匹配到了。(正则表达式的条件判断语法,【|else】可以不写,表示不用else部分的表达式)

对于楼主的问题。应该怎么改?我刚刚刷新了帖子,看到楼主的正则是可以匹配的呀。

汗,贴下PHP的代码吧。


if (preg_match('%<\s*a\s.*?href\s*=\s*(["\'])?(?(1)(.*?)\1|([^\s>]+))[^>]*>(.*?)</a>%si', $subject)) {
	# Successful match
} else {
	# Match attempt failed
}

文章标题跟内容有很大出入,见谅哈。

周末记事-还是我的老爷机

我的老爷机:
06年买的主机箱+飞利浦CRT显示器,07年更换电源风扇,08年上半年分别更换了主板,硬盘,下面年,在内存最低价的时候,99块包快递买了DDR2 667的内存条一根,更新了老内存。09年再次更换了电源风扇,10年由于换地方住,搬家,桌子太靠墙,CRT大屁股显示器放不下,买了优派的22寸液晶显示器,鸟枪换炮了,哈。CRT显示器忍痛割爱以50元的价格卖给了小贩,哎,心痛。接着,鼠标又不灵了,更换了鼠标。从06年坚守到现在的器件只有CPU、主机箱、显卡,光驱、键盘了。

昨天晚上,10点半左右,老爷机又突然间罢工了,顿时,我心急如焚呀,心想即将到来的周末,作为宅男,我不能接受。赶紧调试吧。首先,我以为是天气太热,导致CPU温度过高,激活主板自动保护策略,强制关机的。赶紧把CPU风扇拿出来,放到空调边吹了会,装上去之后,开机,还是不行,主板灯亮了,但是CPU风扇不转,而且,电源也没反映,我愕然了,难道电源又坏了不成?好吧,我认输,周末再说吧,睡觉。

前序:
在老爷机罢工之前,一群内网友求助正则匹配html标签中 table之间的内容,要发现的第一个table之间的内容。字符串大约为

<table class="t1 t2"><tr><td><table class="t1 t2">sssssssssssssssss</table></td></tr><tr>
<td><table class="t1 t2">ss
sssssssssssssss</table></td></tr></table>

同学A提供【<table[\s\S]*</table>】(具体记不清了,大体是这个)

这个正则的匹配结果是不能满足其要求的,很明显,【*】为量词优先,导致贪婪匹配把后面最后一个</table>之前的字符串全部匹配进去了。

见截图:

A同学提供的不严谨的正则表达式

接着,另外以为网友提到,后面加U修饰符。对于这个正则,对于这个要求,加U修饰符能达到目的吗? 答案是可以的。为什么?这种方式可靠吗?有风险吗?有其他方法吗?

先来解释一下U(大写字母U,非小写,小写的是uincode字符模式)是干吗的? 是对“匹配优先量词(贪婪)”与“忽略优先量词(非贪婪)”的取反,也就是说,原来是【*】、【+】的,会按照【*?】、【+?】来执行,如果是【*?】、【+?】的,则按照【*】、【+】来执行,这个修饰符很怪,很乱,如果正则为【\w+】,为了达到“非贪婪匹配”,使用了修饰符U。过几天,你修改了这个正则,修改为【\w+\s+?】,你的本意是在原来的基础上,加上非贪婪的空白字符的匹配,可是,后面的大写字母U修饰符也对你前面的非贪婪取反了,从而,导致匹配错误,所以,建议大家不要使用大写字母的U修饰符,直接在表达式里写好贪婪还是非贪婪。

我的答案是【<table[^>]+?>(?>((?!</table>).(?<!<table))+)</table>】。

我靠,你这这什么玩意?这么乱?这是什么意思?

解释一下:

  1. 【<table[^>]+?>】部分是匹配“<table class=”t1 t2″>”这种table的开头,包括属性的字符串,其中,使用了忽略优先量词(非贪婪),由于其属性不会太多,常规下不超过50个字符串,所以,没有使用固化分组,或者占有优先量词来减少回溯。
  2. 【</table>】(最后面的)是匹配字符串结束的。不用解释了吧。
  3. 【(?>((?!</table>).(?<!<table))+)】这个比较长,主要是匹配<table….到</table>中间的字符串,其中最外面一层【(?>)】是占有优先量词,用来防止不必要的回溯,防止中间超长的字符串造成崩溃。【(?!</table>)】是顺序零宽断言非操作,匹配位置,针对其后面的规则【.】,意思就是说,规则【.】后面不能是字符串“</table>”,同样正则【(?<!<table】是逆序零宽断言非操作,也匹配位置,针对其前面的规则【.】,意思就是说,规则【.】前面不能是字符串“<table…”,这样,两个零宽断言来确定一个字符串,如果是多个字符串,则加上匹配优先量词【+】,但是这个【+】的位置要注意了,不能直接放在【.】的后面,因为要保证每个【.】匹配的字符串,前后的规则都符合两个零宽断言,则需要放在外面,也就是上面所写的那样。

OK,解释完毕,你明白了没?如果没,那是我的错。请留言,批评,我承认错误。(当使用匹配的时候,为了更准确,建议开启不区分大小写模式,以及,多行匹配模式,即修饰符i和修饰符s,小写的s,不是大写的,大写的S是另外的含义<以后写>。)

见截图:

相对比较严谨,比较准确的正则表达式

测试代码:

$str='111<table class="t1 t2"><tr><td><table class="t1 t2">sssssssssssssssss</table></td></tr><tr>
<td><table class="t1 t2">ss
sssssssssssssss</table></td></tr></table>ssss';
preg_match_all('%<table[^>]+?>(?>((?!</table>).(?<!<table))+)</table>%si', $str, $result, PREG_PATTERN_ORDER);
print_r($result[0]);

我的答案可以解决问题吗?可以。准确吗?还行,不太准。为什么?如果table中间有嵌套,则无法正确匹配。如何修复?正则递归匹配。如果递归?

留给你研究吧。CFC4N小试正则表达式里已经介绍了一个关于递归匹配的例子,你可以参考一下。

后续:

老爷机挂了,我一夜没睡着,早上6点10分就醒了,起来之后,捣鼓两下,也没心情了,已经知道大体的症状。手头没工具,也没法测试,算了,索性洗漱去公司。走的早也好,地铁上没多少人,一路不拥挤,唯一的缺点就是没拿到“时代报”。不过到了公司楼下,有派发的,顺手拿了一份。到座位上,心思还在我心爱的老爷机上,也有重新买台新机器的想法,到DELL官方网站上搜了几下,发现价格奇贵,光主机,都4K-5K,看了一会,我就大小这个念头了,作为寄生在这大城市的P民,也别要求太高了。讲究用吧。哎,打算周末冒热抱出去找维修铺,各位,祝福我吧。

PS:下午,拿了早上没看的报纸去厕所,心想,这次终于不干等了,可以打发时间了。当我到厕所里的时候,我震惊了,我靠,马桶上起码5份报纸,而且,还都不一样。汗。

PHP中正则表达式对UNICODE字符码的匹配

酷暑难耐,又在家宅一整天。天气热或许是借口,尽管不热,我也喜欢宅在家。晚上看新三国83集(插一句,最近世界杯很火,可是我不看世界杯。来鄙视我吧),一直看到10点半,突然觉得肚子痛,赶紧直奔厕所。如厕之后,觉得精神抖擞,容光焕发,年轻了二十多岁,打开QQ,看到N多条消息,其中有条是生命如蓝同学留的。打开一看,是PPC的链接,突然觉得我好久没登陆PPC了,想当年……..算了,看链接吧。

打开链接,看到标题是“请教PHP 一个正则匹配的问题”,又是正则表达式,好吧,看下,谁让俺比较喜欢鼓捣正则呢。下面开始正题。
网友ainiaa的问题是

PHP代码如下

$words = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSRUVWXYZ!@#$%^&*()_+-=[]\\,./{}|<>?'\"你好啊我们";
$otherStr=preg_replace("/[chr(128)-chr(256)]+/is"," ",$words);
echo 'otherStr:',$otherStr;

为什么打印的结果会是:
otherStr: ! #$% & {}| ‘”你好啊我们

麻烦问下其中正则表达式 /[chr(128)-chr(256)]+/is 代表什么意思?
如果/[chr(128)-chr(256)]+/is 指的是ascii码在128到256的字符,为什么a-zA-Z这样的字符也被替换掉了,他们的ascii码是小于127的。
最令人郁闷的是为什么ascii码同在0-127区间”#”,”$”,”%”,”&”, “!”,” {“,”}”,”|”,” ‘”,”确没有被替换掉????
更令人感觉神奇的是 如果把正则表达式修改为”/[chr(128)-chr(256)]+/s”的话,输出的结果就变成了: otherStr: defg ijklmnopq stuvwxyz ! #$% & {}| ‘”你好啊我们
只是把正则表达式中的符号‘i’给去掉,结果缺失这样的。 完全的令我理解不了。
不知各位 有何见解????
另附ascii 码 对照表
(这个ASCII码表的图我就不贴了)

回帖中,有个网友说没解析chr(128)这些,并给出了新的解决方法。首先说下此网友回答的是正确的,先不评论他是否“知其然,且知其所以然”,这位网友没有给出错误的原因。

CFC4N来回答一下这位网友:

PHP的正则的preg_match函数用的是PCRE正则引擎,这位网友的代码中,PCRE引擎处理的正则表达式为【/[chr(128)-chr(256)]+/is】,后面的is是什么呢?
在PHP的正则里,边界字符后面的叫模式修饰符。它会告诉引擎如何解析,处理正则。其中i修饰符表示不区分大小写。s表示“点号通配模式”,用来让正则里的元字符点号【.】可以匹配换行符,这个修饰符仅对点号【.】起作用。在这位网友的问题中,修饰符s并不起作用的。

查找原因:
我们在来分析一下这个网友写的正则表达式【[chr(128)-chr(256)]+】,正则表达式的PCRE引擎是如何解释这个正则的呢?首先,我们要知道,在正则表达式中,中括号【[]】表示字符组,字符组中除了连接符【-】只外,都不是元字符,也就是说,都是普通字符,当然,如果连字符出现在第一个,或者不是标识两个字符之间范围的,都是普通的字符横杠“-”罢了。这里的chr(128)只是标识ASCII码为128(确切的说,ASCII码只是0-127个,128到其他的,应该不叫ASCII码了。),但是在正则里,他仍然代表【c、h、r、(、1、2、8、)】(顿号不是,只是区分易读的)这八个字符罢了。这个正则里的连接字符,是哪些范围呢?很明显,这里的连接字符的范围是【)-c】,“)”ASCII码为0×29,也就是十进制的41;“c”的ASCII码为0×63,也就是十进制的99,那么,他这个连接字符的范围就是ASCII 41(chr(41))到ASCII 99(chr(99))之间的字符。也就是说,这位网友的正则的范围是【[hr)-c(]】,就是chr(41)到chr(99)外加hr这两个字母和前面的“(”。
网友第一次测试的时候,有修饰符i,意思就是说,不区分大小写,那么在chr(41)到chr(99)之间的字符,以及这些字符如果有大小写,则包括他们的大小写都符合匹配。都会被替换成空。其第二次测试的时候,去掉了修饰符i,进行了不区分大小写的匹配,由于其范围只到c,但突然,再除了小写字母的“h”、“r”,所以,测试结果会多出“defgijklmnopqstuvwxyz”。所以,他的结果出现了这些差别。

PHP正则表达式匹配UNICODE字符


网友的表达式等同于如下图所示

PHP正则表达式匹配UNICODE字符

解决办法:
错误的原因找出来了,那么,解决的办法呢?
我们先来看看这位网友的需求,他的需求是将unicode(ASCII只是0-127位的,128之后的,应该叫UNICODE码)的chr(128)到chr(255)之间的字符匹配,替换为空罢了。正则表达式里,对十六进制的字符匹配的表示方式有两种,【\u】和【\x{}】,前者只能表示【\u】后面4位的十六进制数值,而后者【\x{}】则可以表示任意多的十六进制位数(写在大括号中)。
那么,这个正则表达式该如何写????

网友的目的是chr(128)到chr(255),那么就是【[\u0080-\u00FF]】或者【[\x{0080}-\x{00FF}]】。
其目的是匹配下图中的红框内字符

UNICODE字符128到255字符集图


提醒一下,PHP里正则匹配unicode字符时,需要使用u修饰符。
根据网友需求,更改正则之后的PHP代码如下:

$words = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSRUVWXYZ!@#$%^&*()_+-=[]\\,./{}|<>?'\"你好啊我们";
$otherStr=preg_replace("//[\x{0080}-\x{00FF}]+/iu"," ",$words);
echo 'otherStr:',$otherStr;

其运行结果是仍然输出那段字符串,为什么呢?因为哪些字符串都不在chr(128)到chr(255)的范围之内。
(测试时,注意文件编码为UTF-8)
以上为鄙人愚见,欢迎批评指正。

PHP正则表达式的效率:回溯与固化分组

PHP正则表达式的回溯与固化分组


上文中,我们聊到了一点关于PHP中(NFA PCRE)正则表达式匹配优先量词,忽略优先量词的匹配原理了。那么上文留下的问题,您的答案是什么呢?
先来看下问题。

字符串

$str = '<script>123456</script>';

正则表达式为

$strRegex1 = '%<script>.+<\/script>%';
$strRegex2 = '%<script>.+?<\/script>%';
$strRegex3 = '%<script>(?:(?!<\/script>).)+<\/script>%';

这三个正则,分别会造成几次回溯呢??

答案:

$strRegex1 = '%<script>.+<\/script>%';    //9次,记得区别转义符号。
$strRegex2 = '%<script>.+?<\/script>%';  //5次
$strRegex3 = '%<script>(?:(?!<\/script>).)+<\/script>%';  //7次

对于第一种贪婪匹配的匹配规则,回溯的9次是正则【】对字符串“”匹配时,构成的回溯,回溯的次数,恰好是字符串的长度。
第二种非贪婪匹配规则,回溯5次,是正则【.+?】对字符串“123456”匹配时构成的回溯。回溯的次数,为字符串长度减去最小次数。也就是6-1=5次。如果正则表达式为【.*?】那么,回溯次数就是6次了。
第三种正则是零宽断言,或者叫环视。(暂且不说。)
在NFA正则引擎中,回溯是他的灵魂,所以,不管是贪婪,非贪婪,环视等写法中肯定会有回溯的出现的,这个我们无法避免(用词不太准确),但是,我们可以减少回溯的次数,或者保护其中一部分匹配的规则不进行回溯。

对于上篇BLOG上提到的鸟哥谈到一个非贪婪引起的大量回溯问题,大家可以知道,回溯,确实是浪费资源的罪魁祸首,那么,我们能否不让其回溯呢?
答案是肯定的,NFA引擎中,有个概念,叫固化分组。引用一下书上的概念

具体来说,使用「(?>…)」的匹配与正常的匹配并无差别,但是如果匹配进行到此结构之后(也就是,进行到闭括号之后),那么此结构体中的所有备用状态都会被放弃。也就是说,在固化分组匹配结束时,它已经匹配的文本已经固化为一个单元,只能作为整体而保留或放弃。括号内的子表达式中未尝试过的备用状态都不复存在了,所以回溯永远也不能选择其中的状态(至少是,当此结构匹配完成时,“锁定(locked in)”在其中的状态)。

那么,固化分组到底有什么用处呢?我们来举个例子。(找不到合适的例子,俺只好借用一下书上的例子了)
比如要处理一批数据,原来格式为123.456,后来因为浮点数显示问题,部分数据格式变为123.456000000789这种,,要求做到只保留小数点后面2-3位,但是,最后一位不能为0,这个正则如何写呢?(下面直接考虑小数点后面的数字),写出正则之后,我们还要用这个正则去匹配数据,把原来的数据替换成匹配的结果。
首先,我们可以立刻写出这样的正则【\.\d\d[1-9]?\d*】,PHP代码为

$str = preg_replace('\.(\d\d[1-9]?)\d*','\\1',$str);  //匹配结果的group1进行反向引用

很明显,这种写法,对于部分数据格式为123.456的这种格式,白白的处理了一遍,为了提高效率,我们还要对这个正则进行处理。从123.456这个字符串跟其他的比较一下,我们发现,是疑问123.456这个数据后面没数字了,所以,白白处理一遍。那好办,我们对这个正则改造一下,把后面的量词*改成+,这样对于123.45 小数点后面1,2位数字的,不会去白白处理,而且,对三位以上数字的,处理正常。其PHP代码为

$str = preg_replace('\.(\d\d[1-9]?)\d+','\\1',$str);

好了,这个正则真的没问题吗??确定吗?上篇博文,我们了解了匹配原理,那么,我们也分析一下这个正则的匹配过程吧。
字符串"123.456",正则表达式为【\.(\d\d[1-9]?)\d+】,我们来看下
首先(小数点前123不说了),【\.】匹配".",匹配成功,把控制权给下一个【\d】,【\d】匹配“4”成功,把控制权给第二个【\d】,这个【\d】匹配“5”成功,然后,把控制权给了【[1-9]?】,由于量词是【?】,正则表达式遵循“量词优先匹配”,而且,此处是【?】,还会留下一个回溯点。然后匹配"6"成功,然后把控制权给【\d+】,【\d+】发现后面没字符了,最遵循“后进先出”规则,回到上一个回溯点,进行匹配,这时,【[1-9]?】会交还出其匹配的字符“6”,【[1-9]?】匹配“6”成功。匹配完成了。大家发现【(\d\d[1-9]?)】匹配的结果确是"45",并不是我们想要的“456”,“6”被【\d+】匹配去了。那么,我们该如何办呢? 能否让【[1-9]?】匹配一旦成功,不进行回溯呢?这就用到了我们上面说的"固化分组", PHP(preg_replace函数)中使用的正则引擎支持固化分组,我们根据固化分组的写法,可以把代码改成如下方式

$str = preg_replace('\.(\d\d(?>[1-9]?))\d+','\\1',$str);

改成这样的话,那字符串“123.456“是不符合要求,不会被匹配的。那我们就可以实现我们的要求了。

从上面的例子中,知道了固化分组的作用,那么对于鸟哥BLOG上写的那个非贪婪的回溯问题,我们能否也对其改造,使得其不回溯呢?
先看下鸟哥给的答案

/<script>[^<]*<\/script>/is

鸟哥写的很精悍。排除“<”之外的所有字符都符合,而且,中间部分不回溯,效率高。可是,如果中间有字符“<“的话(如下代码)

<script>
if a < b
</script>

那鸟哥的这个正则就不能匹配,就不能实现我们想要的功能了。
那我们可以根据 固化分组、环视(零宽断言)来实现这个要求,最后,CFC4N给出的正则以及PHP代码事例如下

$reg = '%<script>(?>[^<]*)(?>(?!</?script>)<[^<]*)*</script>%is';
$str = str_pad("<script>", 111111, "*");    //字符长度大于PHP回溯限制的100000
$str .= 'if a < b ; if b > c;</script>';    //随便加几个包含 < > 的测试字符
$ret = preg_replace($reg, "OK", $str);
print_r($ret);                              //打印结果 OK,证明匹配正确
var_dump(preg_last_error());                //上一次匹配错误。其输出为 int(0)

嗨,同学,你看明白了吗?

以上为小菜CFC4N的愚文,如有错误,欢迎指出。

小议正则表达式效率:贪婪、非贪婪与回溯

前几天看了鸟哥的BLOG上写的关于正则表达式的回溯与递归的限制时,对贪婪、非贪婪产生的回溯有疑问,遂近段时间,仔细的学习研究了一下,现在把经验心得与大家分享一下。
(我日,这里好像被左侧的图挡着了,换行换行凑字数,换行换行凑字数,换行换行凑字数,换行换行凑字数,换行换行凑字数,换行换行凑字数,换行换行凑字数,换行换行凑字数,换行换行凑字数,换行换行凑字数,换行换行凑字数,换行换行凑字数,换行换行凑字数,换行换行凑字数,换行换行凑字数)
先扫盲一下什么是正则表达式的贪婪,什么是非贪婪?或者说什么是匹配优先量词,什么是忽略优先量词?
好吧,我也不知道概念是什么,来举个例子吧。
某同学想过滤之间的内容,那是这么写正则以及程序的。

$str = preg_replace('%<script>.+?</script>%i','',$str);//非贪婪

看起来,好像没什么问题,其实则不然。若

$str = '<script<script>alert(document.cookie)</script>>alert(document.cookie)</script>';

那么经过上面的程序处理,其结果为

$str = '<script<script>alert(document.cookie)</script>>alert(document.cookie)</script>';
$str = preg_replace('%<script>.+?</script>%i','',$str);//非贪婪
print_r($str);
//$str 输出为 <script>alert(document.cookie)</script>

仍然达不到他想要的效果。上面的就是非贪婪,也有的叫惰性。其标志非贪婪的标识为量数元字符后面加? ,比如 +?、*?、??(比较特殊,以后的BLOG中,我会写到)等。即标识非贪婪,如果不写?就是贪婪。比如

$str = '<script<script>alert(document.cookie)</script>>alert(document.cookie)</script>';
$str = preg_replace('%<script>.+</script>%i','',$str);//非贪婪
print_r($str);
//$str 输出为 <script 只有这些了,好像还是不太合适,哈,您知道如何重写那个正则吗?

以上为贪婪,非贪婪的区别介绍。下面,聊下贪婪、非贪婪引起的回溯问题。先看个小例子。
正则表达式为\w*(\d+),字符串为cfc456n,那么,这个正则匹配的$1是多少??

如果您回答是 456,那么,恭喜你,回答了,其结果不是456,而是6,您知道为什么吗?

CFC4N来解释一下,当正则引擎用正则\w*(\d+)去匹配字符串cfc456n时,会先用\w*去匹配字符串cfc456n,首先,\w*会匹配字符串cfc456n的所有字符,然后再交给\d+去匹配剩下的字符串,而剩下的没了,这时,\w*规则会不情愿的吐出一个字符,给\d+去匹配,同时,在吐出字符之前,记录一个点,这个点,就是用于回溯的点,然后\d+去匹配n,发现并不能匹配成功,会再次要求\w*再吐出一个字符,\w*会先再次记录一个回溯的点,再吐出一个字符。这时,\w* 匹配的结果只有cfc45了,已经吐出6n了,\d+再去匹配6,发现匹配成功,则会通知引擎,匹配成功了,就直接显示出来了。所以,(\d+)的结果是6,而不是456。

当上面的正则表达式改为 \w*?(\d+)(注意,此处为非贪婪),字符串仍然为cfc456n,那么,这时候,正则匹配的$1是多少??
甲同学回答:结果是 456。
嗯,是的,正确,是456,CFC4N弱弱的问下,为什么是456 呢?
我在来解释一下 为什么是456
正则表达式有条规则,是量词优先匹配,所以\w*?会先去匹配字符串cfc456,由于\w*?是非贪婪,正则引擎会用表达式\w+?每次仅匹配一个字符串,然后再将控制权交给后面的\d+去匹配下一个字符,同时,记录一个点,用于在匹配不成功的时候,返回这里,再次匹配,也就是回溯点。由于\w后面是量词是*,*表示0到无数次,所以,首先是0次,也就是\w*?匹配个空,记录回溯点,将控制权交给\d+,\d+去匹配cfc456n的第一个字符c,然后,匹配失败,于是乎,接着讲控制权交给\w*?去匹配cfc456n的c,\w*?匹配c成功,由于是非贪婪,所以,他每次只匹配一个字符,记录回溯点,然后再将控制权交给\d+匹配f,接着,\d+匹配f再失败,再把控制权给\w*?,\w*?再匹配c,记录回溯点(这时\w*?匹配结果是cfc了),再把控制权给\d+,\d+去匹配4,匹配成功,然后,由于量词是+,就是1到无数次,所以,接着往后匹配,再匹配5,成功,再接着,再匹配6,成功,再接着,继续匹配操作,下一个字符是n,匹配失败,这时,\d+会吧控制权交出去。由于\d+后面已经没有正则表达式了,所以,整个正则表达式宣告匹配完成,其结果就是 cfc456, 其中第一组结果是456。亲爱的同学,您明白刚刚的题目的结果,为什么是456了吗?

好了,您是否从上面的例子了解了贪婪,非贪婪的匹配原理了?那您是否明白您在什么时候需要使用贪婪,非贪婪去处理您的字符串了?
鸟哥的文章里讲到针对
表达式、程序为

$reg = "/<script>.*?<\/script>/is";
$str = "<script>********</script>"; //长度大于100014
$ret = preg_repalce($reg, "", $str); //返回NULL

其原因就是回溯太多了,直到造成耗尽栈空间爆栈。

再来看个例子。
字符串

$str = '<script>123456</script>';

正则表达式为

$strRegex1 = '%<script>.+<\/script>%';
$strRegex2 = '%<script>.+?<\/script>%';
$strRegex3 = '%<script>(?:(?!<\/script>).)+<\/script>%';

这三个正则,分别会造成几次回溯呢??

答案见下篇 PHP正则表达式的效率:回溯与固化分组

端午节之后一周总结

本周主题:瞎扯淡。

CFC4N 端午节之后一周总结

记下本周(确切的说,是上周)流水账:

  • 在当当网上买了本书《精通正则表达式》,送货速度很快,前天晚上下的订单,第二天中午就送到了。值得表扬。
  • 姐姐来我这玩,带来了一点粽子,蜜枣馅的,还有煎饼等家乡的特产,非常感谢。
  • 陪小外甥再次看了《汽车总动员》,!@#¥#%%¥……¥&%……*……
  • CNXCT.COM域名已经到期,忘记续费了,还好,GLEON同学没忘记,悄悄的续费了。
  • 端午在家看了新买的书《精通正则表达式》,直接从第四章匹配原理开始看,因为我买书的主要原因是想知道匹配原理。还有就是仔细的看了在非贪婪、贪婪的情况下,匹配时候的回溯详情,匹配的优先顺序,回溯的次数,效率等情况。致力做到不光写得出正则,还要写的出高效率的正则表达式
  • 最近比较懒,从过年回来,基本没运动过,上上上周,打了羽毛球。总共运动时间大约20分钟,其中,大部分时间是捡球,羽毛球老是被我抽到树枝上。想办法把羽毛球取下来,也是件麻烦事。周围没有很长的竹竿,树枝之类东西,只好从地上捡起石块砸。结果,右手胳膊酸了一周。昨天,跟几个朋友一起打升级。由于右手一直轻轻的拿鼠标,没有那么用力的捏牌,结果,今天胳膊又酸了。CFC4N想弱弱的问,难道打升级也算运动?
  • 昨天,把床上铺的床单取掉了,直接躺在竹席上,结果,夜里肚子被冰的很痛,一夜没睡好,结果,现在精神状态很差。
  • 今天看到还算是比较轰动的IT新闻,马化腾马云收编草根网站 互联网大佬争夺站长,新闻的主要内容是腾讯收购草根的IT公司康盛创想,其主打产品是Discuz!,不知道是真还是假。很多网友都调侃说到“期待使用’QQ PassPort‘登陆各大BBS”,我稍微想了一下,如果实现,应该也不难。CFC4N想了一个方案,各位网友来批评指正一下。 腾讯可以做一个系统,各大论坛使用者提交论坛的主域名,腾讯再根据论坛使用者的主域名分配一个私钥,然后,论坛使用者将此私钥更改到论坛程序的认证部分文件中。当用户在其中的论坛用QQ登陆的时候,直接跳转到腾讯的登陆页,登陆成功之后,返回到用户登陆前的论坛,论坛再根据私钥来解密cookie,来判断此cookie是否生效。
  • 无意中发现google对我们的BLOG www.cnxct.com 的快照更新了,而某度却还是那样。详情见下图

google更新cnxct.com 的快照截图


BAIDU未更新cnxct.com 的快照截图

呃。差点忘了,刚刚从QQ网络硬盘里,找出N年前收藏的工具SPXInstantScreenCapture,试用截图了,效果就是上图。其中,百度截图那张,有水印。原因是没注册,其实我有别人的注册码,只是我没用,打算中午用OD自己破解一下,很久不搞,基本的反汇编工具都生疏了。温习一下。

coolcode转SyntaxHighlighter与Mysql正则表达式

blog的代码高亮插件原来是coolcode的,coolcode的高亮插件确实很酷,显示效果也很棒,但是占用的位子太大了。最近,我抽空改成SyntaxHighlighter。由于coolcode插件的开头标签是

<coolcode>

或者[coolcode]这样的,而SyntaxHighlighter是

1。
mysql里执行两句sql即可

UPDATE wp_posts SET post_content = REPLACE(post_content,'</coolcode>','[\/code]');  //注意后面多了个反斜杠,记得去掉
UPDATE wp_posts SET post_content = REPLACE(post_content,'[/coolcode]','[\/code]');  //注意后面多了个反斜杠,记得去掉

总结:
本文牵扯的正则表达式并无高级用法,都是平常很简单的用法。关于PCRE引擎正则表达式的递归(迭代),组命名,反向引用,零宽断言等,CFC4N会在以后的时间里,找合适的例子写出来。当然,这些高级用法,CFC4N在帮朋友写的正则表达式里已经用到了,大家可以看看,欢迎批评和指点。
PS:如果需要coolcode转SyntaxHighlighter的完整PHP程序,留言即可,我抽空写出来。

第 1 页,共 2 页12

CNXCT小组的博客 is Stephen Fry proof thanks to caching by WP Super Cache