邵珠庆の日记 生命只有一次,你可以用它来做很多伟大的事情–Make the world a little better and easier


1811月/07

常用的四十个网站制作技巧

发布在 邵珠庆

1.   oncontextmenu= "window.event.returnValue=false "   将彻底屏蔽鼠标右键
<table   border   oncontextmenu=return(false)> <td> no </table>   可用于Table

2.   <body   onselectstart= "return   false ">   取消选取、防止复制

3.   onpaste= "return   false "   不准粘贴

4.   oncopy= "return   false; "   oncut= "return   false; "   防止复制

5.   <link   rel= "Shortcut   Icon "   href= "favicon.ico ">   IE地址栏前换成自己的图标

6.   <link   rel= "Bookmark "   href= "favicon.ico ">   可以在收藏夹中显示出你的图标

7.   <input   style= "ime-mode:disabled ">   关闭输入法

8.   永远都会带着框架
<script   language= "JavaScript "> <!--
if   (window   ==   top)top.location.href   =   "frames.htm ";   //frames.htm为框架网页
//   --> </script>

9.   防止被人frame
<SCRIPT   LANGUAGE=JAVASCRIPT> <!--
if   (top.location   !=   self.location)top.location=self.location;
//   --> </SCRIPT>

10.   网页将不能被另存为
<noscript> <iframe   src= "/blog/*.html> "; </iframe> </noscript>

11.   <input   type=button   value=查看网页源代码
onclick= "window.location   =   "view-source: "+   "http://www.williamlong.info " ">

12.删除时确认
<a   href= "javascript:if(confirm( "确实要删除吗? "))location= "boos.asp?&areyou=删除&page=1 " "> 删除 </a>

13.   取得控件的绝对位置
//Javascript
<script   language= "Javascript ">
function   getIE(e){
var   t=e.offsetTop;
var   l=e.offsetLeft;
while(e=e.offsetParent)
alert( "top= "+t+ "/nleft= "+l);
}
</script>

//VBScript
<script   language= "VBScript "> <!--
function   getIE()
dim   t,l,a,b
set   a=document.all.img1
t=document.all.img1.offsetTop
l=document.all.img1.offsetLeft
while   a.tagName <> "BODY "
set   a   =   a.offsetParent
t=t+a.offsetTop
l=l+a.offsetLeft
wend
msgbox   "top= "&t&chr(13)& "left= "&l,64, "得到控件的位置 "
end   function
--> </script>

14.   光标是停在文本框文字的最后
<script   language= "javascript ">
function   cc()
{
var   e   =   event.srcElement;
var   r   =e.createTextRange();
r.moveStart( "character ",e.value.length);
r.collapse(true);
r.select();
}
</script>
<input   type=text   name=text1   value= "123 "   onfocus= "cc() ">

15.   判断上一页的来源
javascript:
document.referrer

16.   最小化、最大化、关闭窗口
<object   id=hh1   classid= "clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11 ">
<param   name= "Command "   value= "Minimize "> </object>
<object   id=hh2   classid= "clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11 ">
<param   name= "Command "   value= "Maximize "> </object>
<OBJECT   id=hh3   classid= "clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11 ">
<PARAM   NAME= "Command "   VALUE= "Close "> </OBJECT>
<input   type=button   value=最小化   onclick=hh1.Click()>
<input   type=button   value=最大化   onclick=hh2.Click()>
<input   type=button   value=关闭   onclick=hh3.Click()>
本例适用于IE

17.屏蔽功能键Shift,Alt,Ctrl
<script>
function   look(){
if(event.shiftKey)
alert( "禁止按Shift键! ");   //可以换成ALT CTRL
}
document.onkeydown=look;
</script>

18.   网页不会被缓存
<META   HTTP-EQUIV= "pragma "   CONTENT= "no-cache ">
<META   HTTP-EQUIV= "Cache-Control "   CONTENT= "no-cache,   must-revalidate ">
<META   HTTP-EQUIV= "expires "   CONTENT= "Wed,   26   Feb   1997   08:21:57   GMT ">
或者 <META   HTTP-EQUIV= "expires "   CONTENT= "0 ">

19.怎样让表单没有凹凸感?
<input   type=text   style= "border:1   solid   #000000 ">

<input   type=text   style= "border-left:none;   border-right:none;   border-top:none;   border-bottom:

1   solid   #000000 "> </textarea>

20. <div> <span> & <layer> 的区别?
<div> (division)用来定义大段的页面元素,会产生转行
<span> 用来定义同一行内的元素,跟 <div> 的唯一区别是不产生转行
<layer> 是ns的标记,ie不支持,相当于 <div>

21.让弹出窗口总是在最上面:
<body   onblur= "this.focus(); ">

22.不要滚动条?
让竖条没有:
<body   style= "overflow:scroll;overflow-y:hidden ">
</body>
让横条没有:
<body   style= "overflow:scroll;overflow-x:hidden ">
</body>
两个都去掉?更简单了
<body   scroll= "no ">
</body>

23.怎样去掉图片链接点击后,图片周围的虚线?
<a   href= "# "   onFocus= "this.blur() "> <img   src= "/blog/logo.jpg "   border=0> </a>

24.电子邮件处理提交表单
<form   name= "form1 "   method= "post "   action= "mailto:****@***.com "   enctype= "text/plain ">
<input   type=submit>
</form>

25.在打开的子窗口刷新父窗口的代码里如何写?
window.opener.location.reload()

26.如何设定打开页面的大小
<body   onload= "top.resizeTo(300,200); ">
打开页面的位置 <body   onload= "top.moveBy(300,200); ">

27.在页面中如何加入不是满铺的背景图片,拉动页面时背景图不动
<STYLE>
body
{background-image:url(/blog/logo.gif);   background-repeat:no-repeat;
background-position:center;background-attachment:   fixed}
</STYLE>

28.   检查一段字符串是否全由数字组成
<script   language= "Javascript "> <!--
function   checkNum(str){return   str.match(//D/)==null}
alert(checkNum( "1232142141 "))
alert(checkNum( "123214214a1 "))
//   --> </script>

29.   获得一个窗口的大小
document.body.clientWidth;   document.body.clientHeight

30.   怎么判断是否是字符
if   (/[^/x00-/xff]/g.test(s))   alert( "含有汉字 ");
else   alert( "全是字符 ");

31.TEXTAREA自适应文字行数的多少
<textarea   rows=1   name=s1   cols=27   onpropertychange= "this.style.posHeight=this.scrollHeight ">
</textarea>

32.   日期减去天数等于第二个日期
<script   language=Javascript>
function   cc(dd,dadd)
{
//可以加上错误处理
var   a   =   new   Date(dd)
a   =   a.valueOf()
a   =   a   -   dadd   *   24   *   60   *   60   *   1000
a   =   new   Date(a)
alert(a.getFullYear()   +   "年 "   +   (a.getMonth()   +   1)   +   "月 "   +   a.getDate()   +   "日 ")
}
cc( "12/23/2002 ",2)
</script>

33.   选择了哪一个Radio
<HTML> <script   language= "vbscript ">
function   checkme()
for   each   ob   in   radio1
if   ob.checked   then   window.alert   ob.value
next
end   function
</script> <BODY>
<INPUT   name= "radio1 "   type= "radio "   value= "style "   checked> Style
<INPUT   name= "radio1 "   type= "radio "   value= "barcode "> Barcode
<INPUT   type= "button "   value= "check "   onclick= "checkme() ">
</BODY> </HTML>

34.脚本永不出错
<SCRIPT   LANGUAGE= "JavaScript ">
<!--   Hide
function   killErrors()   {
return   true;
}
window.onerror   =   killErrors;
//   -->
</SCRIPT>

35.ENTER键可以让光标移到下一个输入框
<input   onkeydown= "if(event.keyCode==13)event.keyCode=9 ">

问题点数:0 回复次数:22 显示所有回复显示星级回复显示楼主回复
netnpc
cooly
等 级:
发表于:2007-11-14 12:05:271楼 得分:0
36.   检测某个网站的链接速度:
把如下代码加入 <body> 区域中:
<script   language=Javascript>
tim=1
setInterval( "tim++ ",100)
b=1
var   autourl=new   Array()
autourl[1]= "www.njcatv.net "
autourl[2]= "javacool.3322.NET "
autourl[3]= "www.sina.com.cn "
autourl[4]= "www.nuaa.edu.cn "
autourl[5]= "www.cctv.com "
function   butt(){
document.write( " <form   name=autof> ")
for(var   i=1;i <autourl.length;i++)
document.write( " <input   type=text   name=txt "+i+ "   size=10   value= "/blog/测试中......> "   =》 <input   type=text
name=url "+i+ "   size=40>   =》 <input   type=button   value=Go

onclick=window.open(this.form.url "+i+ ".value)> <br/> ")
document.write( " <input   type=submit   value=刷新> </form> ")
}
butt()
function   auto(url)
else

b++
}
function   run(){for(var   i=1;i <autourl.length;i++)document.write( " <img   src=http:// "+autourl+ "/ "+Math.random()+ "   width=1   height=1

onerror=auto( "http:// "+autourl+ " ")> ")}
run() </script>

37.   各种样式的光标
auto   :标准光标
default   :标准箭头
hand   :手形光标
wait   :等待光标
text   :I形光标
vertical-text   :水平I形光标
no-drop   :不可拖动光标
not-allowed   :无效光标
help   :?帮助光标
all-scroll   :三角方向标
move   :移动标
crosshair   :十字标
e-resize
n-resize
nw-resize
w-resize
s-resize
se-resize
sw-resize

38.页面进入和退出的特效
进入页面 <meta   http-equiv= "Page-Enter "   content= "revealTrans(duration=x,   transition=y) ">
推出页面 <meta   http-equiv= "Page-Exit "   content= "revealTrans(duration=x,   transition=y) ">
这个是页面被载入和调出时的一些特效。duration表示特效的持续时间,以秒为单位。transition表示使用哪种特效,取值为1-23:
0   矩形缩小
1   矩形扩大
2   圆形缩小
3   圆形扩大
4   下到上刷新
5   上到下刷新
6   左到右刷新
7   右到左刷新
8   竖百叶窗
9   横百叶窗
10   错位横百叶窗
11   错位竖百叶窗
12   点扩散
13   左右到中间刷新
14   中间到左右刷新
15   中间到上下
16   上下到中间
17   右下到左上
18   右上到左下
19   左上到右下
20   左下到右上
21   横条
22   竖条
23   以上22种随机选择一种

39.在规定时间内跳转
<META   http-equiv=V= "REFRESH "   content= "5;URL=http://www.williamlong.info ">

40.网页是否被检索
<meta   name= "ROBOTS "   content= "属性值 ">
其中属性值有以下一些:
属性值为 "all ":   文件将被检索,且页上链接可被查询;
属性值为 "none ":   文件不被检索,而且不查询页上的链接;
属性值为 "index ":   文件将被检索;
属性值为 "follow ":   查询页上的链接;
属性值为 "noindex ":   文件不检索,但可被查询链接;
属性值为 "nofollow ":   文件不被检索,但可查询页上的链接。

最大化窗口?
<script   language= "JavaScript ">
<!--
self.moveTo(0,0)
self.resizeTo(screen.availWidth,screen.availHeight)
//-->
</script>
解决问题:由于层与下拉框之间的优先级是:下拉框   >   层,因此在显示的时候,会因为优先级的次序而会出现如上问题。(如果几个元素都是层的话,我们可以通过层的   z-index   属性来设置)解决办法就是:给层中放一个优先级比下拉框更高的元素(iframe),从而解决此问题!具体解决代码如下:

<div   id= "menu "   style= "position:absolute;   visibility:hidden;   top:20px;   left:20px;   width:100px;   height:200px;   background-color:#6699cc; ">
<table>
<tr> <td> item   1 </td> </tr>
<tr> <td> item   2 </td> </tr>
<tr> <td> item   3 </td> </tr>
<tr> <td> item   4 </td> </tr>
<tr> <td> item   5 </td> </tr>
</table>
<iframe   src= "/blog/javascript:false "   style= "position:absolute;   visibility:inherit;   top:0px;   left:0px;   width:100px;   height:200px;   z-index:-1;   filter= 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0) '; "> </iframe>
</div>

<a   href= "# "   onclick= "document.getElementById( 'menu ').style.visibility= 'visible ' "> menu </a>

<form>
<select> <option> A   form   selection   list </option> </select>
</form>

输入框也可以做的很漂亮了
<div   align= "center "> <input   type= "hidden "   name= "hao "   value= "yes ">
外向数: <input
name=answer
style= "color:   rgb(255,0,0);   border-left:   medium   none;   border-right:   medium   none;   border-top:   medium   none;   border-bottom:   1px   solid   rgb(192,192,192) ">
没回答的题数: <input
name=unanswer   id= "unanswer "
style= "color:   rgb(255,0,0);   border-left:   medium   none;   border-right:   medium   none;   border-top:   medium   none;   border-bottom:   1px   solid   rgb(192,192,192) ">
<br/>
总得分:
<input
name=score   id= "score "
style= "color:   rgb(255,0,0);   border-left:   medium   none;   border-right:   medium   none;   border-top:   medium   none;   border-bottom:   1px   solid   rgb(192,192,192) ">
结    论:
<input
name=xgjg   id= "xgjg "
style= "color:   rgb(255,0,0);   border-left:   medium   none;   border-right:   medium   none;   border-top:   medium   none;   border-bottom:   1px   solid   rgb(192,192,192) ">
<br/>
<br/>

<input   onClick=processForm(this.form)   style= "FONT-FAMILY:   宋体;   FONT-SIZE:   9pt "   type=button   value=查看结果   name= "button ">
<input   type= "reset "   name= "Submit "   value= "重做 ">
</div>
注意:修改 <body> 为 <body   onload= "max.Click() "> 即为打开最大

化窗口,而如果改为 <body   onload= "min.Click() "> 就变为窗口一打开就最小化

<object   id= "min "   type= "application/x-oleobject "   classid= "clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11 ">
<param   name= "Command "   value= "Minimize ">
</object>   <object   id= "max "   type= "application/x-oleobject "   classid= "clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11 ">
<param   name= "Command "   value= "Maximize ">
</object>
</body>

页面自动刷新(说明)

当你做网页时,是不是有的时候想让你的网页自动不停刷新,或者过一段时间自动跳转到另外一个你自己设定的页面?其实实现这个效果非常地简单,而且这个效果甚至不能称之为特效。你只要把如下代码加入你的网页中就可以了。

1,页面自动刷新:把如下代码加入 <head> 区域中 <meta   http-equiv= "refresh "   content= "20 "> ,其中20指每隔20秒刷新一次页面.

2,页面自动跳转:把如下代码加入 <head> 区域中 <meta   http-equiv= "refresh "   content= "20;url=http://www.williamlong.info "> ,其中20指隔20秒后跳转到http://www.williamlong.info页面。

页面自动关闭

5000是指时间 <body   onLoad= "setTimeout(window.close,   5000) ">

弹出窗口自动关闭

10秒后弹出窗口自动关闭

注意:在新的tan.htm的body中要加   <onLoad= "closeit() ">
head

<script   language= "JavaScript ">

<!--

var   gt   =   unescape( '%3e ');

var   popup   =   null;

var   over   =   "Launch   Pop-up   Navigator ";

popup   =   window.open( ' ',   'popupnav ',   'width=225,height=235,resizable=1,scrollbars=auto ');

if   (popup   !=   null)   {

if   (popup.opener   ==   null)   {

popup.opener   =   self;

}

popup.location.href   =   'tan.htm ';

}

//   -->

</script>
<body> 注意:这段代码是在新建文件中的
<script   language= "JavaScript ">

function   closeit()

</script>

这个可不是 <iframe> (引用)呀。是直接调用的。以下代码加入 <body> 区域

<object   type= "text/x-scriptlet "   width= "800 "     height= "1000 "   data= "../index.htm ">
</object>

16月/07

正则表达式高级技巧背后的关键概念

发布在 邵珠庆

英文原文来自Smashing Magazine 。由笨活儿 翻译。转载请注明出处。

正则表达式(Regular Expression, abbr. regex ) 功能强大,能够用于在一大串字符里找到所需信息。它利用约定俗成的字符结构表达式来发生作用。不幸的是,简单的正则表达式对于一些高级运用,功能远远不够。若要进行筛选的结构比较复杂,你可能就需要用到高级正则表达式

本文为您介绍正则表达式的高级技巧 。我们筛选出了八个常用的概念,并配上实例解析,每个例子都是满足某种复杂要求的简单写法。如果你对正则的基本概念尚缺乏了解,请先阅读这篇文章 ,或者这个教程 ,或者维基条目

这里的正则语法适用于PHP,与Perl 兼容。

 

1. 贪婪/懒惰

Greed

所有能多次限定的正则运算符都是贪婪的。他们尽可能多 地匹配目标字符串,也就是说匹配结果会尽可能地长 。不幸的是,这种做法并不总是我们想要的。因此,我们添加“懒惰”限定符来解决问题。在各个贪婪运算符后添加“?”能让表达式只匹配尽可能短 的长度。另外,修改器“U”也能惰化能多次限定的运算符。理解贪婪与懒惰的区别是运用高级正则表达式的基础。

贪婪操作符

操作符 * 匹配之前的表达式零次或零次以上。它是一个贪婪操作符。请看下面的例子:

1 preg_match( '/<h1>.*< //h1>/' , '</h1><h1>这是一个标题。</h1>
2 <h1>这是另一个。</h1>', $matches );

句点(.)能代表除换行符外的任意字符。上面的正则表达式匹配 h1 标签以及标签内的所有内容。它用句点(.)和星号(*)来匹配标签内的所有内容。匹配结果如下:

1 <H1>这是一个标题。</H1>
2 <H1>这是另一个。</H1>

整个字串都被返回。* 操作符会连续匹配所有内容—— 甚至包括中间的 h1 闭合标签。因为它是贪婪的,匹配整个字串是符合其利益最大化原则。

懒惰操作符

把上面的式子稍作修改,加上一个问号(?),能让表达式变懒惰:

1 /<h1>.*?< //h1>/</h1>

这样它会觉得,只需匹配到第一个 h1 结尾标签就完成任务了。

另一个有着类似属性的贪婪操作符是 {n,} 。它代表之前的匹配模式重复n次或n次以上,如果没有加上问号,它会寻找尽可能多的重复次数,加上的话,则会尽可能少重复(当然也就是“重复n次”最少)。

1 # 建立字串
2 $str = 'hihihi oops hi' ;
3 # 使用贪婪的{n,}操作符进行匹配
4 preg_match( '/(hi){2,}/' , $str , $matches );  # matches[0] 将是 'hihihi'
5 # 使用堕化了的 {n,}? 操作符匹配
6 preg_match( '/(hi){2,}?/' , $str , $matches );  # matches[0] 将是 'hihi'

2. 回返引用(Back referencing)

Back Referencing

有什么用?

回返引用(Back referencing) 一般被翻译成“反向引用”、“后向引用”、“向后引用”,个人觉得“回返引用”更为贴切[笨活儿 ]。它是在正则表达式内部引用之前捕获到的内容 的方法。例如,下面这个简单例子的目的是匹配出引号内部的内容:

01 # 建立匹配数组
02 $matches = array ();
03  
04 # 建立字串
05 $str = "/"This is a 'string'/"" ;
06  
07 # 用正则表达式捕捉内容
08 preg_match( "/(/"|').*?(/"|')/" , $str , $matches );
09  
10 # 输出整个匹配字串
11 echo   $matches [0];

它会输出:

1 "This is a'

显然,这并不是我们想要的内容。

这个表达式从开头的双引号开始匹配,遭遇单引号之后就错误地结束了匹配。这是因为表达式里说:("|') ,也就是双引号(" )和单引号(')均可。要修正这个问题,你可以用到回返引用。表达式/1,/2,…,/9 是对前面已捕获到的各个子内容的编组序号,能作为对这些编组的“指针”而被引用。在此例中,第一个被匹配的引号就由1 代表。

如何运用?

将上面的例子中,后面的闭合引号替换为1:

1 preg_match( '/("|/').*?/1/' , $str , $matches );

这会正确地返回字串:

1 "This is a 'string'"

译注思考题:

如果是中文引号,前引号和后引号不是同一个字符,怎么办?

还记得PHP函数 preg_replace 吗?其中也有回返引用。只不过我们没有用 /1 … /9,而是用了 $1 … $9 … $n (此处任意数目均可)作为回返指针。例如,如果你想把所有的段落标签

都替换成文本:

1 $text = preg_replace( '/<p>(.*?)< //p>/' ,
2 '&lt;p&gt;$1&lt;/p&gt;' , $html );

参数$1是一个回返引用,代表段落标签

内部的文字,并插入到替换后的文本里。这种简便易用的表达式写法为我们提供了一个获取已匹配文字的简单方法,甚至在替换文本时也能使用。

3. 已命名捕获组(Named Groups)

当在一个表达式内多次用到回调引用时,很容易就把事情搞混淆,要弄清那些数字(/1 … /9)都代表哪一个子内容是件很麻烦的事。回调引用的一个替代方法是使用带名字的捕获组(下文简称“有名组”)。有名组使用(?Ppattern) 来设定,name代表组名,pattern是配合该有名组的正则结构。请看下面的例子:

1 /(?P<quote>"|').*?(?P=quote)/

上式中,quote就是组名,"|' 是改组匹配内容的正则。后面的(?P=quote)是在调用组名为quote的有名组。这个式子的效果和上面的回调引用实例一样,只不过是用了有名组来实现。是不是更加易读易懂了?

有名组也能用于处理已匹配内容之数组的内部数据。赋予特定正则的组名也能作为所匹配到的内容在数组内部的索引词。

1 preg_match( '/(?P<quote>"|/')/' , " 'String' ", $matches );
2  
3 # 下面的语句输出“'”(不包括双引号)
4 echo $matches [1];
5  
6 # 使用组名调用,也会输出“'”
7 echo $matches [ 'quote' ];

所以,有名组并不只是让写代码更容易,它也能用于组织代码。

4. 字词边界(Word Boundaries)

Word Boundaries

字词边界 是字串里的字词字符(包括字母、数字和下划线,自然也包括汉字)和非字词字符之间的位置。其特殊之处就在于,它并不匹配某个实在的字符。它的长度是 。 /b 匹配所有字词边界。

不幸的是,字词边界一般都被忽视掉了,大部分人都没有在意他的现实意义。 例如,如果你想要匹配单词“import”:

1 /import/

注意了!正则表达式有时候很调皮的。下面的字串也能和上面的式子匹配成功:

1 important

你或许觉得,只要在import前后加上空格,不就可以匹配这个独立的单词了:

1 / import /

那如果遇上这种情况呢:

1 The trader voted for the import

当 import 这个词在字串开头或者结尾时,修改后的表达式仍然不能用。因此,考虑各种情况是必须的:

1 /(^import | import | import$)/i

别慌,还没完呢。如果遇到标点符号了呢?就为了满足这一个单词的匹配,你的正则可能就需要这样写:

1 /(^import(:|;|,)? | import(:|;|,)? | import(/.|/?|!)?$)/i

对于只匹配一个单词来说,这样做实在是有点大动干戈了。正因如此,字词边界才显得意义重大。要适应上述要求,以及很多其他情况变种 ,有了字符边界,我们所需写的代码只是:

1 //bimport/b/

上面所有情况都得到了解决。/ b 的灵活性就在于,它是一个没有长度的匹配。它只匹配两个实际字符之间想象出的位置。它检查两个相邻字符是否是一个为单字,另一个为非单字。情况符合,就返回匹配。如果遇到了单词的开头或结尾, /b 会把它当成是非单词字符对待。由于import里面的 i 仍然被看成是单词字符,import 就被匹配出来了。

注意,与/b 相对,我们还有/B ,此操作符匹配两个单字或者两个非单字之间的位置。因此,如果你想匹配在某个单词内部的‘hi’,可以使用:

1 /Bhi/B

“this”、“hight”,都会返回匹配,而“hi there”则不会返回匹配。

5. 最小组团(Atomic Groups)

Advanced Operators

最小组团 是无捕捉的特殊正则表达式分组。通常用来提高正则表达式的效能,也能用于消除特定匹配。一个最小组团可以用(?>pattern) 来定义,其中pattern是匹配式。

1 /(?>his|this)/

当正则引擎针对最小组团进行匹配时,它会跳过组团内标记的回溯位置。以单词“smashing”为例,当用上面的正则表达式匹配时,正则引擎会先尝试在“smashing”里寻找“his”。显然,找不到任何匹配。此时,最小组团就发挥作用了:正则引擎会放弃所有回溯位置。也就是说,它不会尝试再从“smashing”里查找“this”。为什么要这样设置?因为“his”都没有返回匹配结果,包含有“his”的“this”当然就更匹配不了了!

上面的例子并没有什么实用性,我们用/t?his?/ 也能达到效果。再看看下面的例子:

1 //b(engineer|engrave| end )/b/

如果把“engineering”拿去匹配,正则引擎会先匹配到“engineer”,但接下来就遇到了字词边界,/b ,所以匹配不成功。然后,正则引擎又会尝试在字串里寻找下一个匹配内容:engrave。匹配到eng的时候,后面的又对不上了,匹配失败。最后,尝试“end”,结果同样是失败。仔细观察,你会发现,一旦engineer匹配失败,并且都抵达了字词边界,“engrave”和“end”这两个词就已经不可能匹配成功了。这两个词都比engineer短小,正则引擎不应该再多做无谓的尝试。

1 //b(?>engineer|engrave| end )/b/

上面的替代写法更能节省正则引擎的匹配时间,提高代码的工作效率。

6. 递归(Recursion)

Recursion

递归(Recursion) 用于匹配嵌套结构,例如括弧嵌套, (this (that)),HTML标签嵌套

。我们使用(?R) 来代表递归过程中的子模式。下面是一个匹配嵌套括弧的例子:

1 //(((?>[^()]+)|(?R))*/)/

最外层使用了反义符的括号“( ”匹配嵌套结构的开端。然后是一个多选项操作符( * | * ) ,可能匹配除括号外的所有字符 “(?>[^()]+)”,也可能是通过子模式“(?R) ”来再次匹配整个表达式。请注意,这个操作符会尽量多地匹配所有嵌套。

递归的另一个实例如下:

1 /< ([/w]+).*?>((?>[^<>]+)|((?R)))*< ///1>/

以上表达式综合运用了字符分组,贪婪操作符、回溯,以及最小化组团来匹配嵌套标签。第一个括弧内分组([/w]+) 匹配出标签名,用于接下来的应用。若找到这尖括号样式的标签,则尝试寻找标签内容的剩余部分。下一个括弧括起来的子表达式和上一个实例非常相似:要么匹配不包括尖括号的所有字符 ?>[^<>]+ ,要么递归匹配整个表达式(?R) 。表达式最后的< //1> 代表闭合标签。

7. 回调(Callbacks)

Callbacks

匹配结果中的特定内容有时可能会需要某种特别的修改。要应用多重而复杂的修改,正则表达式的回调 就有了用武之地。回调是用于函数preg_replace_callback 中的动态修改字串的方式。你可以为preg_replace_callback 指定某个函数为参数,此函数能接收匹配结果数组为参数,并将数组修改后返回,作为替换的结果。

例如,我们想将某字串中的字母全部转变成大写。十分不巧,PHP没有直接转化字母大小写的正则操作符。要完成这项任务,就可以用到正则回调。首先,表达式要匹配出所有需要被大写的字母:

1 //b/w/

上式同时使用了字词边界和字符类。光有这个式子还不够,我们还需要一个回调函数:

1 function upper_case( $matches ) {
2 return strtoupper ( $matches [0] );
3 }

函数upper_case 接收匹配结果数组,并将整个匹配结果转化成大写。 在此例中,$matches[0] 代表需要被大写化的字母。然后,我们再利用preg_replace_callback 实现回调:

1 preg_replace_callback( '//b/w/' , 'upper_case' , $str );

一个简单的回调即有这般强大的力量。

8. 注释(Commenting)

Commenting

注释 不用来匹配字串,但确实是正则表达式中最重要的部分。当正则越写越深入,越写越复杂,要推译出究竟什么东西被匹配就会变得越来越困难。在正则表达式中间加上注释,是最小化将来的迷糊和困惑的最佳方式。

要在正则表达式内部加上注释,使用(?#comment) 格式。把“comment”替换成你的注释语句:

1 /(?#数字)/d/

如果你打算把代码公之于众,为正则表达式加上注释就显得尤为重要。这样别人才能更容易看懂和修改你的代码。和其他场合的注释一样,这样做也能为你重访自己以前写的程序时提供方便。

考虑使用“x”或“(?x)”修改器来格式化注释。这个修改器让正则引擎忽略表达式参数之间的空格。“有用的”空格仍然能够通过[ ] 或/s ,或者/ (反义符加空格)来匹配。

1 /
2 /d    #digit
3 [ ]   #space
4 /w+   #word
5 /x

上面的代码与下面的式子作用一样:

请时刻注意代码的可读性。