Skip to content

重读《红楼梦》之三

宝玉与黛玉读西厢记 

话说林黛玉只因昨夜晴雯不开门一事,错疑在宝玉身上。至次日又可巧遇见饯花之期,正是一腔无明正未发泄,又勾起伤春愁思,因把些残花落瓣去掩埋,由不得感花伤己,哭了几声,便随口念了几句。不想宝玉在山坡上听见,先不过点头感叹,次后听到“侬今葬花人笑痴,他年葬侬知是谁”,“一朝春尽红颜老,花落人亡两不知”等句,不觉恸倒山坡之上,怀里兜的落花撒了一地。试想林黛玉的花颜月貌,将来亦到无可寻觅之时,宁不心碎肠断!既黛玉终归无可寻觅之时,推之于他人,如宝钗,香菱,袭人等,亦可到无可寻觅之时矣。宝钗等终归
无可寻觅之时,则自己又安在哉?且自身尚不知何在何往,则斯处,斯园,斯花,斯柳,又不知当属谁姓矣!──因此一而二,二而三,反复推求了去,真不知此时此际欲为何等蠢物,杳无所知,逃大造,出尘网,使可解释这段悲伤。正是:
花影不离身左右,鸟声只在耳东西。
那林黛玉正自伤感,忽听山坡上也有悲声,心下想道:“人人都笑我有些痴病,难道还有一个痴子不成?”想着,抬头一看,见是宝玉。林黛玉看见,便道:“啐!我道是谁,原来是这个狠心短命的……”刚说到“短命“二字,又把口掩
住,长叹了一声,自己抽身便走了。
这里宝玉悲恸了一回,忽然抬头不见了黛玉,便知黛玉看见他躲开了,自己也觉无味,抖抖土起来,下山寻归旧路,往怡红院来。可巧看见林黛玉在前头走,连忙赶上去,说道:“你且站住。我知你不理我,我只说一句话,从今后撂开手。”
林黛玉回头看见是宝玉,待要不理他,听他说“只说一句话,从此撂开手”,这话里有文章,少不得站住说道:“有一句话,请说来。”宝玉笑道:“两句话,说了你听不听?”黛玉听说,回头就走。宝玉在身后面叹道:“既有今日,何必
当初!”林黛玉听见这话,由不得站住,回头道:“当初怎么样?今日怎么样?”
宝玉叹道:“当初姑娘来了,那不是我陪着顽笑?凭我心爱的,姑娘要,就拿去,我爱吃的,听见姑娘也爱吃,连忙干干净净收着等姑娘吃。一桌子吃饭,一床上睡觉。丫头们想不到的,我怕姑娘生气,我替丫头们想到了。我心里想着:姊妹
们从小儿长大,亲也罢,热也罢,和气到了儿,才见得比人好。如今谁承望姑娘人大心大,不把我放在眼睛里,倒把外四路的什么宝姐姐凤姐姐的放在心坎儿上,倒把我三日不理四日不见的。我又没个亲兄弟亲姊妹。──虽然有两个,你难道
不知道是和我隔母的?我也和你似的独出,只怕同我的心一样。谁知我是白操了这个心,弄的有冤无处诉!”说着不觉滴下眼泪来。
黛玉耳内听了这话,眼内见了这形景,心内不觉灰了大半,也不觉滴下泪来,低头不语。宝玉见他这般形景,遂又说道:“我也知道我如今不好了,但只凭着怎么不好,万不敢在妹妹跟前有错处。便有一二分错处,你倒是或教导我,戒我下次,或骂我两句,打我两下,我都不灰心。谁知你总不理我,叫我摸不着头脑,少魂失魄,不知怎么样才好。就便死了,也是个屈死鬼,任凭高僧高道忏悔也不能超生,还得你申明了缘故,我才得托生呢!”
黛玉听了这个话,不觉将昨晚的事都忘在九霄云外了,便说道:“你既这么说,昨儿为什么我去了,你不叫丫头开门?”宝玉诧异道:“这话从那里说起?我要是这么样,立刻就死了!”林黛玉啐道:“大清早起死呀活的,也不忌讳。
你说有呢就有,没有就没有,起什么誓呢。”宝玉道:“实在没有见你去。就是宝姐姐坐了一坐,就出来了。”林黛玉想了一想,笑道:“是了。想必是你的丫头们懒待动,丧声歪气的也是有的。”宝玉道:“想必是这个原故。等我回去问了是谁,教训教训他们就好了。”黛玉道:“你的那些姑娘们也该教训教训,只是我论理不该说。今儿得罪了我的事小,倘或明儿宝姑娘来,什么贝姑娘来,也得罪了,事情岂不大了。”说着抿着嘴笑。宝玉听了,又是咬牙,又是笑。

      每次看到这一段时,总忍不住会心一笑。误会产生时的气愤,误会过程中的痛苦,误会进行中的为难,误会消解时如释重负,如此的种种,经历过,才更知感情事的艰辛。

重读《红楼梦》之二

巧者劳而智者忧,无能者无所求,饱食而遨游,汎若不系之舟。

      不系之舟,比喻极是恰当。

花谢花飞花满天,红消香断有谁怜?
游丝软系飘春榭,落絮轻沾扑绣帘。
闺中女儿惜春暮,愁绪满怀无释处,
手把花锄出绣闺,忍踏落花来复去。
柳丝榆荚自芳菲,不管桃飘与李飞。
桃李明年能再发,明年闺中知有谁?
三月香巢已垒成,梁间燕子太无情!
明年花发虽可啄,却不道人去梁空巢也倾。
一年三百六十日,风刀霜剑严相逼,
明媚鲜妍能几时,一朝飘泊难寻觅。
花开易见落难寻,阶前闷杀葬花人,
独倚花锄泪暗洒,洒上空枝见血痕。
杜鹃无语正黄昏,荷锄归去掩重门。
青灯照壁人初睡,冷雨敲窗被未温。
怪奴底事倍伤神,半为怜春半恼春:
怜春忽至恼忽去,至又无言去不闻。
昨宵庭外悲歌发,知是花魂与鸟魂?
花魂鸟魂总难留,鸟自无言花自羞。
愿奴胁下生双翼,随花飞到天尽头。
天尽头,何处有香丘?
未若锦囊收艳骨,一抔净土掩风流。
质本洁来还洁去,强于污淖陷渠沟。
尔今死去侬收葬,未卜侬身何日丧?
侬今葬花人笑痴,他年葬侬知是谁?
试看春残花渐落,便是红颜老死时。
一朝春尽红颜老,花落人亡两不知!

      在看电视剧版的红楼的时候,最喜的莫过于美声唱的葬花吟了,尤其是“天尽头,何处有香丘”一句,最是催人泪下。如今,花依旧,葬花人不再,时光依旧,故事不再。

重读《红楼梦》

黛玉与宝钗

满纸荒唐言,一把辛酸泪!
都云作者痴,谁解其中味?

      大约每一个人读红楼,最初的印象都会来自这一绝吧。这是传说中红楼的主旨所在,也是那些持“自传说”,即红楼乃曹氏自传的人的立足点。我喜欢这首诗,是因为它的第一句对仗别致,后一句反问深刻,如此而已。

世事洞明皆学问,人情练达即文章。

      此对意蕴甚远,我还曾经将它写在自己的笔记本的扉页上,以示推崇。

早知日后闲争气,岂肯今朝错读书。

      这句写的是秦钟被宝玉拉到私塾一起读书,岂料后来争气以至身死。一个“早知”,写出多少后悔的心思!后面宝黛拌嘴时,宝玉也曾说“早知今日,何必当初!”

红楼一梦

不经意间,又看完了一遍红楼梦。

说“又”,是因为,在中学的时候,已经看了好多遍了;说“不经意”,因为自己并不像以前那样,有很多很大块的时间,整天捧着书看。只有昨天,因为回学校上课,没有去上班,结果看了一整天,终于在晚上九点前,结束了“战斗”:部分因为澡堂快关门了,主要原因还是后面的十几回确实不太感兴趣,确切的说,自从第九十八回黛玉去世以后,后面的在我看来,都成了鸡肋了罢。

只是这次看,又比以往有许多不同:对诗词,有了许多兴趣;对人物,也有了一些认识;最喜欢的,还是黛玉;最推崇的,还是湘云。相同的是,对于某些时候某些人的对白,依旧需要细细咀嚼,细细品味,才懂得如此说的好处来。

摘一些喜欢的诗词曲赋,录几段可心的描述对白,也不枉看了一通红楼了。

有些人,有些事,有些话,有些爱

     有些人一直没机会见,等有机会见了,却又犹豫了,相见不如不见。

  有些事一直没机会做,等有机会了,却不想再做了。

  有些话埋藏在心中好久,没机会说,等有机会说的时候,却说不出口了。

  有些爱一直没机会爱,等有机会了,已经不爱了。

  有些人很多机会相见的,却总找借口推脱,想见的时候已经没机会了。

  有些话有很多机会说的,却想着以后再说,要说的时候,已经没机会了。

  有些事有很多机会做的,却一天一天推迟,想做的时候却发现没机会了。

  有些爱给了你很多机会,却不在意没在乎,想重视的时候已经没机会爱了。

  人生有时候,总是很讽刺。

  一转身可能就是一世。

  说好永远的,不知怎么就散了。最后自己想来想去竟然也搞不清当初是什么原因分开彼此的。然后,你忽然醒悟,感情原来是这么脆弱的。经得起风雨,却经不起平凡;风雨同船,天晴便各自散了。也许只是赌气,也许只是因为小小的事。幻想着和好的甜蜜,或重逢时的拥抱,那个时候会是边流泪边捶打对方,还傻笑着。该是多美的画面。

  没想到的是,一别竟是一辈子了。

  于是,各有各的生活,各自爱着别的人。曾经相爱,现在已互不相干。

  即使在同一个小小的城市,也不曾再相逢。某一天某一刻,走在同一条街,也看不见对方。

  先是感叹,后来是无奈。

  也许你很幸福,因为找到另一个适合自己的人。

  也许你不幸福,因为可能你这一生就只有那个人真正用心在你身上。

  很久很久,没有对方的消息,也不再想起这个人,也是不想再想 。

部门变动

      不久前才从视频搜索换到网页大搜索,这相当于换了一下小部门,直接主管也变了。幸运的是,还是可以跟以前那一帮人一起吃饭。所以,每天的午餐时间,成了联系大家的纽带。

      但今天,部门主管过来告诉我说,另一个部门需要从我们部门调一个过去帮忙,时间为一个月。而我们部门内部商量的结果是派我过去!但是,新部门在18层,那我以后还能继续跟大家一起吃饭么?

      有一个疑问:我的邮件签名需要把“研发-搜索”换成“互动社区”之类的么?

      PS.一个大好消息是:因为我被借到另外一个部门,我在搜索项目的所有服务器上的权限被取消,所以,后台系统我没有做完的统计不能继续做了,直接转手给下一个“Lucky guy” :-)

正则表达式之二

[原创文章,转载请保留或注明出处:http://www.regexlab.com/zh/regtopic.htm]

引言
本文将逐步讨论一些正则表达式的使用话题。本文为本站基础篇之后的扩展,在阅读本文之前,建议先阅读正则表达式参考文档一文。
———————————————————————-

1. 表达式的递归匹配
有时候,我们需要用正则表达式来分析一个计算式中的括号配对情况。比如,使用表达式 “\( [^)]* \)” 或者 “\( .*? \)” 可以匹配一对小括号。但是如果括号 内还嵌有一层括号的话 ,如 “( ( ) )”,则这种写法将不能够匹配正确,得到的结果是 “( ( )” 。类似情况的还有 HTML 中支持嵌套的标签如 “ ” 等。本节将要讨论的是,想办法把有嵌套的的成对括号或者成对标签匹配出来。

匹配未知层次的嵌套:

有的正则表达式引擎,专门针对这种嵌套提供了支持。并且在栈空间允许的情况下,能够支持任意未知层次的嵌套:比如 Perl,PHP,GRETA 等。在 PHP 和 GRETA 中,表达式中使用 “(?R)” 来表示嵌套部分。

匹配嵌套了未知层次的 “小括号对” 的表达式写法如下:”\( ([^()] | (?R))* \)”。

[Perl 和 PHP 的示例代码]

匹配有限层次的嵌套:

对于不支持嵌套的正则表达式引擎,只能通过一定的办法来匹配有限层次的嵌套。思路如下:

第一步,写一个不能支持嵌套的表达式:”\( [^()]* \)”,”((?!).)*“。 这两个表达式在匹配有嵌套的文本时,只匹配最内层。

第二步,写一个可匹配嵌套一层的表达式:”\( ([^()] | \( [^()]* \))* \)”。这个表达式在匹配嵌套层数大于一时,只能匹配最里面的两层,同时,这个表达式也能匹配没有嵌套的文本或者嵌套的最里层。

匹配嵌套一层的 “” 标签,表达式为:”((?!).|(((?!).)*))*”。这个表达式在匹配 “” 嵌套层数大于一的文本时,只匹配最里面的两层。

第三步,找到匹配嵌套(n)层的表达式 与 嵌套(n-1)层的表达式之间的关系。比如,能够匹配嵌套(n)层的表达式为:

[标记头] ( [匹配 [标记头] 和 [标记尾] 之外的表达式] | [匹配 n-1 层的表达式] )* [标记尾]

回头来看前面编写的“可匹配嵌套一层”的表达式:

  \( ( [^()] | \(([^()])*\) )* \)
( (?!). | (((?!).)*) )*

PHP 和 GRETA 的简便之处在于,匹配嵌套(n-1)层的表达式用 (?R) 表示:
\( ( [^()] | (?R) )* \)

第四步,依此类推,可以编写出匹配有限(n)层的表达式。这种方式写出来的表达式,虽然看上去很长,但是这种表达式经过编译后,匹配效率仍然是很高的。
——————————————————————————–

2. 非贪婪匹配的效率
可能有不少的人和我一样,有过这样的经历:当我们要匹配类似 ”

内容 ([^))* ((?! .*? 内容 ([^))* ((?! .*?" 的效率是完全相同的。

情况二:如果一个表达式中有多个未知匹配次数的表达式,应防止进行不必要的尝试匹配。

比如,对表达式 "(.*?)" 来说, 如果前面部分表达式在遇到 "" 时匹配成功后,而后边的 "(.*?)" 却匹配失败,将导致第一个 ".*?" 增加匹配次数再尝试。而对于表达式真正目的,让第一个 ".*?" 增加匹配成“vbscript'>”是不对的,因此这种尝试是不必要的尝试。

因此,对依靠边界来识别的表达式,不要让未知匹配次数的部分跨过它的边界。前面的表达式中,第一个 ".*?" 应该改写成 "[^']*”。后边那个 “.*?” 的右边再没有未知匹配次数的表达式,因此这个非贪婪匹配没有效率陷阱。于是,这个匹配脚本块的表达式,应该写成:”(.*?)” 更好。

关于正则表达式(Zz)

[原创文章,转载请保留或注明出处:http://www.regexlab.com/zh/regref.htm]

引言
正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来:(1)检查一个串中是否含有符合某个规则的子串,并且可以得到这个子串;(2)根据匹配规则对字符串进行灵活的替换操作。

正则表达式学习起来其实是很简单的,不多的几个较为抽象的概念也很容易理解。之所以很多人感觉正则表达式比较复杂,一方面是因为大多数的文档没有做到由浅入深地讲解,概念上没有注意先后顺序,给读者的理解带来困难;另一方面,各种引擎自带的文档一般都要介绍它特有的功能,然而这部分特有的功能并不是我们首先要理解的。

文章中的每一个举例,都可以点击进入到测试页面进行测试。闲话少说,开始。
——————————————————————————–

1. 正则表达式规则
1.1 普通字符
字母、数字、汉字、下划线、以及后边章节中没有特殊定义的标点符号,都是”普通字符”。表达式中的普通字符,在匹配一个字符串的时候,匹配与之相同的一个字符。

举例1:表达式 “c”,在匹配字符串 “abcde” 时,匹配结果是:成功;匹配到的内容是:”c”;匹配到的位置是:开始于2,结束于3。(注:下标从0开始还是从1开始,因当前编程语言的不同而可能不同)

举例2:表达式 “bcd”,在匹配字符串 “abcde” 时,匹配结果是:成功;匹配到的内容是:”bcd”;匹配到的位置是:开始于1,结束于4。
——————————————————————————–

1.2 简单的转义字符
一些不便书写的字符,采用在前面加 “\” 的方法。这些字符其实我们都已经熟知了。

表达式
可匹配

\r, \n
代表回车和换行符

\t
制表符

\\
代表 “\” 本身
还有其他一些在后边章节中有特殊用处的标点符号,在前面加 “\” 后,就代表该符号本身。比如:^, $ 都有特殊意义,如果要想匹配字符串中 “^” 和 “$” 字符,则表达式就需要写成 “\^” 和 “\$”。

表达式
可匹配

\^
匹配 ^ 符号本身

\$
匹配 $ 符号本身

\.
匹配小数点(.)本身
这些转义字符的匹配方法与 “普通字符” 是类似的。也是匹配与之相同的一个字符。

举例1:表达式 “\$d”,在匹配字符串 “abc$de” 时,匹配结果是:成功;匹配到的内容是:”$d”;匹配到的位置是:开始于3,结束于5。
——————————————————————————–

1.3 能够与 ‘多种字符’ 匹配的表达式
正则表达式中的一些表示方法,可以匹配 ‘多种字符’ 其中的任意一个字符。比如,表达式 “\d” 可以匹配任意一个数字。虽然可以匹配其中任意字符,但是只能是一个,不是多个。这就好比玩扑克牌时候,大小王可以代替任意一张牌,但是只能代替一张牌。

表达式
可匹配

\d
任意一个数字,0~9 中的任意一个

\w
任意一个字母或数字或下划线,也就是 A~Z,a~z,0~9,_ 中任意一个

\s
包括空格、制表符、换页符等空白字符的其中任意一个

.
小数点可以匹配除了换行符(\n)以外的任意一个字符
举例1:表达式 “\d\d”,在匹配 “abc123″ 时,匹配的结果是:成功;匹配到的内容是:”12″;匹配到的位置是:开始于3,结束于5。

举例2:表达式 “a.\d”,在匹配 “aaa100″ 时,匹配的结果是:成功;匹配到的内容是:”aa1″;匹配到的位置是:开始于1,结束于4。
——————————————————————————–

1.4 自定义能够匹配 ‘多种字符’ 的表达式
使用方括号 [ ] 包含一系列字符,能够匹配其中任意一个字符。用 [^ ] 包含一系列字符,则能够匹配其中字符之外的任意一个字符。同样的道理,虽然可以匹配其中任意一个,但是只能是一个,不是多个。

表达式
可匹配

[ab5@]
匹配 “a” 或 “b” 或 “5″ 或 “@”

[^abc]
匹配 “a”,”b”,”c” 之外的任意一个字符

[f-k]
匹配 “f”~”k” 之间的任意一个字母

[^A-F0-3]
匹配 “A”~”F”,”0″~”3″ 之外的任意一个字符
举例1:表达式 “[bcd][bcd]” 匹配 “abc123″ 时,匹配的结果是:成功;匹配到的内容是:”bc”;匹配到的位置是:开始于1,结束于3。

举例2:表达式 “[^abc]” 匹配 “abc123″ 时,匹配的结果是:成功;匹配到的内容是:”1″;匹配到的位置是:开始于3,结束于4。
——————————————————————————–

1.5 修饰匹配次数的特殊符号
前面章节中讲到的表达式,无论是只能匹配一种字符的表达式,还是可以匹配多种字符其中任意一个的表达式,都只能匹配一次。如果使用表达式再加上修饰匹配次数的特殊符号,那么不用重复书写表达式就可以重复匹配。

使用方法是:”次数修饰”放在”被修饰的表达式”后边。比如:”[bcd][bcd]” 可以写成 “[bcd]{2}”。

表达式
作用

{n}
表达式重复n次,比如:”\w{2}” 相当于 “\w\w”;”a{5}” 相当于 “aaaaa”

{m,n}
表达式至少重复m次,最多重复n次,比如:”ba{1,3}”可以匹配 “ba”或”baa”或”baaa”

{m,}
表达式至少重复m次,比如:”\w\d{2,}”可以匹配 “a12″,”_456″,”M12344″…

?
匹配表达式0次或者1次,相当于 {0,1},比如:”a[cd]?”可以匹配 “a”,”ac”,”ad”

+
表达式至少出现1次,相当于 {1,},比如:”a+b”可以匹配 “ab”,”aab”,”aaab”…

*
表达式不出现或出现任意次,相当于 {0,},比如:”\^*b”可以匹配 “b”,”^^^b”…
举例1:表达式 “\d+\.?\d*” 在匹配 “It costs $12.5″ 时,匹配的结果是:成功;匹配到的内容是:”12.5″;匹配到的位置是:开始于10,结束于14。

举例2:表达式 “go{2,8}gle” 在匹配 “Ads by goooooogle” 时,匹配的结果是:成功;匹配到的内容是:”goooooogle”;匹配到的位置是:开始于7,结束于17。
——————————————————————————–

1.6 其他一些代表抽象意义的特殊符号
一些符号在表达式中代表抽象的特殊意义:

表达式
作用

^
与字符串开始的地方匹配,不匹配任何字符

$
与字符串结束的地方匹配,不匹配任何字符

\b
匹配一个单词边界,也就是单词和空格之间的位置,不匹配任何字符
进一步的文字说明仍然比较抽象,因此,举例帮助大家理解。

举例1:表达式 “^aaa” 在匹配 “xxx aaa xxx” 时,匹配结果是:失败。因为 “^” 要求与字符串开始的地方匹配,因此,只有当 “aaa” 位于字符串的开头的时候,”^aaa” 才能匹配,比如:”aaa xxx xxx”。

举例2:表达式 “aaa$” 在匹配 “xxx aaa xxx” 时,匹配结果是:失败。因为 “$” 要求与字符串结束的地方匹配,因此,只有当 “aaa” 位于字符串的结尾的时候,”aaa$” 才能匹配,比如:”xxx xxx aaa”。

举例3:表达式 “.\b.” 在匹配 “@@@abc” 时,匹配结果是:成功;匹配到的内容是:”@a”;匹配到的位置是:开始于2,结束于4。
进一步说明:”\b” 与 “^” 和 “$” 类似,本身不匹配任何字符,但是它要求它在匹配结果中所处位置的左右两边,其中一边是 “\w” 范围,另一边是 非”\w” 的范围。

举例4:表达式 “\bend\b” 在匹配 “weekend,endfor,end” 时,匹配结果是:成功;匹配到的内容是:”end”;匹配到的位置是:开始于15,结束于18。

一些符号可以影响表达式内部的子表达式之间的关系:

表达式
作用

|
左右两边表达式之间 “或” 关系,匹配左边或者右边

( )
(1). 在被修饰匹配次数的时候,括号中的表达式可以作为整体被修饰
(2). 取匹配结果的时候,括号中的表达式匹配到的内容可以被单独得到
举例5:表达式 “Tom|Jack” 在匹配字符串 “I’m Tom, he is Jack” 时,匹配结果是:成功;匹配到的内容是:”Tom”;匹配到的位置是:开始于4,结束于7。匹配下一个时,匹配结果是:成功;匹配到的内容是:”Jack”;匹配到的位置时:开始于15,结束于19。

举例6:表达式 “(go\s*)+” 在匹配 “Let’s go go go!” 时,匹配结果是:成功;匹配到内容是:”go go go”;匹配到的位置是:开始于6,结束于14。

举例7:表达式 “¥(\d+\.?\d*)” 在匹配 “$10.9,¥20.5″ 时,匹配的结果是:成功;匹配到的内容是:”¥20.5″;匹配到的位置是:开始于6,结束于10。单独获取括号范围匹配到的内容是:”20.5″。
——————————————————————————–

2. 正则表达式中的一些高级规则
2.1 匹配次数中的贪婪与非贪婪
在使用修饰匹配次数的特殊符号时,有几种表示方法可以使同一个表达式能够匹配不同的次数,比如:”{m,n}”, “{m,}”, “?”, “*”, “+”,具体匹配的次数随被匹配的字符串而定。这种重复匹配不定次数的表达式在匹配过程中,总是尽可能多的匹配。比如,针对文本 “dxxxdxxxd”,举例如下:

表达式
匹配结果

(d)(\w+)
“\w+” 将匹配第一个 “d” 之后的所有字符 “xxxdxxxd”

(d)(\w+)(d)
“\w+” 将匹配第一个 “d” 和最后一个 “d” 之间的所有字符 “xxxdxxx”。虽然 “\w+” 也能够匹配上最后一个 “d”,但是为了使整个表达式匹配成功,”\w+” 可以 “让出” 它本来能够匹配的最后一个 “d”
由此可见,”\w+” 在匹配的时候,总是尽可能多的匹配符合它规则的字符。虽然第二个举例中,它没有匹配最后一个 “d”,但那也是为了让整个表达式能够匹配成功。同理,带 “*” 和 “{m,n}” 的表达式都是尽可能地多匹配,带 “?” 的表达式在可匹配可不匹配的时候,也是尽可能的 “要匹配”。这 种匹配原则就叫作 “贪婪” 模式 。

非贪婪模式:

在修饰匹配次数的特殊符号后再加上一个 “?” 号,则可以使匹配次数不定的表达式尽可能少的匹配,使可匹配可不匹配的表达式,尽可能的 “不匹配”。这种匹配原则叫作 “非贪婪” 模式,也叫作 “勉强” 模式。如果少匹配就会导致整个表达式匹配失败的时候,与贪婪模式类似,非贪婪模式会最小限度的再匹配一些,以使整个表达式匹配成功。举例如下,针对文本 “dxxxdxxxd” 举例:

表达式
匹配结果

(d)(\w+?)
“\w+?” 将尽可能少的匹配第一个 “d” 之后的字符,结果是:”\w+?” 只匹配了一个 “x”

(d)(\w+?)(d)
为了让整个表达式匹配成功,”\w+?” 不得不匹配 “xxx” 才可以让后边的 “d” 匹配,从而使整个表达式匹配成功。因此,结果是:”\w+?” 匹配 “xxx”
更多的情况,举例如下:

举例1:表达式 ”

(.*) aa bb aa bb (.*?) aa bb (.*?) ” 与 “” 不配对,则会匹配失败;如果改成其他配对,也可以匹配成功。
——————————————————————————–

2.3 预搜索,不匹配;反向预搜索,不匹配
前面的章节中,我讲到了几个代表抽象意义的特殊符号:”^”,”$”,”\b”。它们都有一个共同点,那就是:它们本身不匹配任何字符,只是对 “字符串的两头” 或者 “字符之间的缝隙” 附加了一个条件。理解到这个概念以后,本节将继续介绍另外一种对 “两头” 或者 “缝隙” 附加条件的,更加灵活的表示方法。

正向预搜索:”(?=xxxxx)”,”(?!xxxxx)”

格式:”(?=xxxxx)”,在被匹配的字符串中,它对所处的 “缝隙” 或者 “两头” 附加的条件是:所在缝隙的右侧,必须能够匹配上 xxxxx 这部分的表达式。因为它只是在此作为这个缝隙上附加的条件,所以它并不影响后边的表达式去真正匹配这个缝隙之后的字符。这就类似 “\b”,本身不匹配任何字符。”\b” 只是将所在缝隙之前、之后的字符取来进行了一下判断,不会影响后边的表达式来真正的匹配。

举例1:表达式 “Windows (?=NT|XP)” 在匹配 “Windows 98, Windows NT, Windows 2000″ 时,将只匹配 “Windows NT” 中的 “Windows “,其他的 “Windows ” 字样则不被匹配。

举例2:表达式 “(\w)((?=\1\1\1)(\1))+” 在匹配字符串 “aaa ffffff 999999999″ 时,将可以匹配6个”f”的前4个,可以匹配9个”9″的前7个。这个表达式可以读解成:重复4次以上的字母数字,则匹配其剩下最后2位之前的部分。当然,这个表达式可以不这样写,在此的目的是作为演示之用。

格式:”(?!xxxxx)”,所在缝隙的右侧,必须不能匹配 xxxxx 这部分表达式。

举例3:表达式 “((?!\bstop\b).)+” 在匹配 “fdjka ljfdl stop fjdsla fdj” 时,将从头一直匹配到 “stop” 之前的位置,如果字符串中没有 “stop”,则匹配整个字符串。

举例4:表达式 “do(?!\w)” 在匹配字符串 “done, do, dog” 时,只能匹配 “do”。在本条举例中,”do” 后边使用 “(?!\w)” 和使用 “\b” 效果是一样的。

反向预搜索:”(?

琐碎的日记

      拿着一个可以运行的程序,放数据的硬盘却坏了。程序还有段错误问题,也没有办法调试了——因为没有数据。

      临时又恶补了一下GDB和VIM,需要学的东西实在太多了,这才后悔当初没有扎实的学。C语言的基础的东西也有很多不熟练的,类似于fread,fseek等文件操作,如果熟练一点的话,也不至于落到今天这样的尴尬局面呢。指针,结构体,唉,就更不用说了。惭愧。

      继续努力。

GDB调试精粹及使用实例(收藏)

                                   

一:列文件清单
1. List
(gdb) list line1,line2

二:执行程序
要想运行准备调试的程序,可使用run命令,在它后面可以跟随发给该程序的任何参数,包括标准输入和标准输出说明符()和外壳通配符(*、?、[、])在内。
如果你使用不带参数的run命令,gdb就再次使用你给予前一条run命令的参数,这是很有用的。
利用set args 命令就可以修改发送给程序的参数,而使用show args 命令就可以查看其缺省参数的列表。
gdb)set args –b –x
(gdb) show args
backtrace命令为堆栈提供向后跟踪功能。
Backtrace 命令产生一张列表,包含着从最近的过程开始的所以有效过程和调用这些过程的参数。

三:显示数据
利用print 命令可以检查各个变量的值。
(gdb) print p (p为变量名)
whatis 命令可以显示某个变量的类型
(gdb) whatis p
type = int *

print 是gdb的一个功能很强的命令,利用它可以显示被调试的语言中任何有效的表达式。表达式除了包含你程序中的变量外,还可以包含以下内容:
l 对程序中函数的调用
(gdb) print find_entry(1,0)
l 数据结构和其他复杂对象
(gdb) print *table_start
$8={e=reference=’00’,location=0×0,next=0×0}
l 值的历史成分
(gdb)print $1 ($1为历史记录变量,在以后可以直接引用 $1 的值)
l 人为数组
人为数组提供了一种去显示存储器块(数组节或动态分配的存储区)内容的方法。早期的调试程序没有很好的方法将任意的指针换成一个数组。就像对待参数一样,让我们查看内存中在变量h后面的10个整数,一个动态数组的语法如下所示:
base@length
因此,要想显示在h后面的10个元素,可以使用h@10:
(gdb)print h@10
$13=(-1,345,23,-234,0,0,0,98,345,10)

四:断点(breakpoint)
break命令(可以简写为b)可以用来在调试的程序中设置断点,该命令有如下四种形式:
l break line-number 使程序恰好在执行给定行之前停止。
l break function-name 使程序恰好在进入指定的函数之前停止。
l break line-or-function if condition 如果condition(条件)是真,程序到达指定行或函数时停止。
l break routine-name 在指定例程的入口处设置断点

如果该程序是由很多原文件构成的,你可以在各个原文件中设置断点,而不是在当前的原文件中设置断点,其方法如下:
(gdb) break filename:line-number
(gdb) break filename:function-name

要想设置一个条件断点,可以利用break if命令,如下所示:
(gdb) break line-or-function if expr
例:
(gdb) break 46 if testsize==100

从断点继续运行:countinue 命令
五.断点的管理

1. 显示当前gdb的断点信息:
(gdb) info break
他会以如下的形式显示所有的断点信息:
Num Type Disp Enb Address What
1 breakpoint keep y 0x000028bc in init_random at qsort2.c:155
2 breakpoint keep y 0x0000291c in init_organ at qsort2.c:168
(gdb)
2.删除指定的某个断点:
(gdb) delete breakpoint 1
该命令将会删除编号为1的断点,如果不带编号参数,将删除所有的断点
(gdb) delete breakpoint
3.禁止使用某个断点
(gdb) disable breakpoint 1
该命令将禁止断点 1,同时断点信息的 (Enb)域将变为 n
4.允许使用某个断点
(gdb) enable breakpoint 1
该命令将允许断点 1,同时断点信息的 (Enb)域将变为 y
5.清除原文件中某一代码行上的所有断点
(gdb)clean number
注:number 为原文件的某个代码行的行号
六.变量的检查和赋值
l whatis:识别数组或变量的类型
l ptype:比whatis的功能更强,他可以提供一个结构的定义
l set variable:将值赋予变量
l print 除了显示一个变量的值外,还可以用来赋值

七.单步执行
l next
不进入的单步执行
l step
进入的单步执行
如果已经进入了某函数,而想退出该函数返回到它的调用函数中,可使用命令finish
八.函数的调用
l call name 调用和执行一个函数
(gdb) call gen_and_sork( 1234,1,0 )
(gdb) call printf(“abcd”)
$1=4
l finish 结束执行当前函数,显示其返回值(如果有的话)

九.机器语言工具
有一组专用的gdb变量可以用来检查和修改计算机的通用寄存器,gdb提供了目前每一台计算机中实际使用的4个寄存器的标准名字:
l $pc : 程序计数器
l $fp : 帧指针(当前堆栈帧)
l $sp : 栈指针
l $ps : 处理器状态

十.信号
gdb通常可以捕捉到发送给它的大多数信号,通过捕捉信号,它就可决定对于正在运行的进程要做些什么工作。例如,按CTRL-C将中断信号发送给gdb,通常就会终止gdb。但是你或许不想中断gdb,真正的目的是要中断gdb正在运行的程序,因此,gdb要抓住该信号并停止它正在运行的程序,这样就可以执行某些调试操作。

Handle命令可控制信号的处理,他有两个参数,一个是信号名,另一个是接受到信号时该作什么。几种可能的参数是:
l nostop 接收到信号时,不要将它发送给程序,也不要停止程序。
l stop 接受到信号时停止程序的执行,从而允许程序调试;显示一条表示已接受到信号的消息(禁止使用消息除外)
l print 接受到信号时显示一条消息
l noprint 接受到信号时不要显示消息(而且隐含着不停止程序运行)
l pass 将信号发送给程序,从而允许你的程序去处理它、停止运行或采取别的动作。
l nopass 停止程序运行,但不要将信号发送给程序。
例如,假定你截获SIGPIPE信号,以防止正在调试的程序接受到该信号,而且只要该信号一到达,就要求该程序停止,并通知你。要完成这一任务,可利用如下命令:
(gdb) handle SIGPIPE stop print
请注意,UNIX的信号名总是采用大写字母!你可以用信号编号替代信号名
如果你的程序要执行任何信号处理操作,就需要能够测试其信号处理程序,为此,就需要一种能将信号发送给程序的简便方法,这就是signal命令的任务。该 命令的参数是一个数字或者一个名字,如SIGINT。假定你的程序已将一个专用的SIGINT(键盘输入,或CTRL-C;信号2)信号处理程序设置成采 取某个清理动作,要想测试该信号处理程序,你可以设置一个断点并使用如下命令:
gdb) signal 2
continuing with signal SIGINT(2)
该程序继续执行,但是立即传输该信号,而且处理程序开始运行.

十一. 原文件的搜索
search text:该命令可显示在当前文件中包含text串的下一行。
Reverse-search text:该命令可以显示包含text 的前一行。

十二.UNIX接口
shell 命令可启动UNIX外壳,CTRL-D退出外壳,返回到 gdb.

十三.命令的历史
为了允许使用历史命令,可使用 set history expansion on 命令
(gdb) set history expansion on

小结:常用的gdb命令
backtrace 显示程序中的当前位置和表示如何到达当前位置的栈跟踪(同义词:where)
breakpoint 在程序中设置一个断点
cd 改变当前工作目录
clear 删除刚才停止处的断点
commands 命中断点时,列出将要执行的命令
continue 从断点开始继续执行
delete 删除一个断点或监测点;也可与其他命令一起使用
display 程序停止时显示变量和表达时
down 下移栈帧,使得另一个函数成为当前函数
frame 选择下一条continue命令的帧
info 显示与该程序有关的各种信息
jump 在源程序中的另一点开始运行
kill 异常终止在gdb 控制下运行的程序
list 列出相应于正在执行的程序的原文件内容
next 执行下一个源程序行,从而执行其整体中的一个函数
print 显示变量或表达式的值
pwd 显示当前工作目录
pype 显示一个数据结构(如一个结构或C++类)的内容
quit 退出gdb
reverse-search 在源文件中反向搜索正规表达式
run 执行该程序
search 在源文件中搜索正规表达式
set variable 给变量赋值
signal 将一个信号发送到正在运行的进程
step 执行下一个源程序行,必要时进入下一个函数
undisplay display命令的反命令,不要显示表达式
until 结束当前循环
up 上移栈帧,使另一函数成为当前函数
watch 在程序中设置一个监测点(即数据断点)
whatis 显示变量或函数类型
****************************************************
 GNU的调试器称为gdb,该程序是一个交互式工具,工作在字符模式。在 X Window 系统中,有一个gdb的前端图形工具,称为xxgdb。gdb 是功能强大的调试程序,可完成如下的调试任务: 
  * 设置断点; 
  * 监视程序变量的值; 
  * 程序的单步执行; 
  * 修改变量的值。 
  在可以使用 gdb 调试程序之前,必须使用 -g 选项编译源文件。可在 makefile 中如下定义 CFLAGS 变量: 
   CFLAGS = -g 
   运行 gdb 调试程序时通常使用如下的命令: 
   gdb progname 

  在 gdb 提示符处键入help,将列出命令的分类,主要的分类有: 
  * aliases:命令别名 
  * breakpoints:断点定义; 
  * data:数据查看; 
  * files:指定并查看文件; 
  * internals:维护命令; 
  * running:程序执行; 
  * stack:调用栈查看; 
  * statu:状态查看; 
  * tracepoints:跟踪程序执行。 
  键入 help 后跟命令的分类名,可获得该类命令的详细清单。 

gdb 的常用命令 
命令 解释 
  break NUM 在指定的行上设置断点。 
  bt 显示所有的调用栈帧。该命令可用来显示函数的调用顺序。 
  clear 删除设置在特定源文件、特定行上的断点。其用法为clear FILENAME:NUM 
  continue 继续执行正在调试的程序。该命令用在程序由于处理信号或断点而 导致停止运行时。 
  display EXPR 每次程序停止后显示表达式的值。表达式由程序定义的变量组成。 
  file FILE 装载指定的可执行文件进行调试。 
  help NAME 显示指定命令的帮助信息。 
  info break 显示当前断点清单,包括到达断点处的次数等。 
  info files 显示被调试文件的详细信息。 
  info func 显示所有的函数名称。 
  info local 显示当函数中的局部变量信息。 
  info prog 显示被调试程序的执行状态。 
  info var 显示所有的全局和静态变量名称。 
  kill 终止正被调试的程序。 
  list 显示源代码段。 
  make 在不退出 gdb 的情况下运行 make 工具。 
  next 在不单步执行进入其他函数的情况下,向前执行一行源代码。 
  print EXPR 显示表达式 EXPR 的值。 

******gdb 使用范例************************ 
—————– 
清单 一个有错误的 C 源程序 bugging.c 
代码: 

—————– 
1 #include  

3 static char buff [256]; 
4 static char* string; 
5 int main () 
6 { 
7   printf (“Please input a string: ”); 
8   gets (string);   
9   printf (“\nYour string is: %s\n”, string); 
10 } 
 

—————– 
  上面这个程序非常简单,其目的是接受用户的输入,然后将用户的输入打印出来。该程序使用了一个未经过初始化的字符串地址  string,因此,编译并运行之后,将出现 Segment Fault 错误: 
$ gcc -o bugging -g bugging.c 
$ ./bugging 
Please input a string: asfd 
Segmentation fault (core dumped) 
为了查找该程序中出现的问题,我们利用 gdb,并按如下的步骤进行: 
1.运行 gdb bugging 命令,装入 bugging 可执行文件; 
2.执行装入的 bugging 命令 run; 
3.使用 where 命令查看程序出错的地方; 
4.利用 list 命令查看调用 gets 函数附近的代码; 
5.唯一能够导致 gets 函数出错的因素就是变量 string。用print命令查看 string 的值; 
6.在 gdb 中,我们可以直接修改变量的值,只要将 string 取一个合法的指针值就可以了,为此,我们在第8行处设置断点 break 8; 
7.程序重新运行到第 8行处停止,这时,我们可以用 set variable 命令修改 string 的取值; 
8.然后继续运行,将看到正确的程序运行结果。