深柳堂

探索匹配中文的正则表达式

按:本文使用的RegexBuddy为3.1.0(完全)版,并非最新版3.1.1(截至2008.08.23)。需要该版本的请在这篇文章后留言。

注:参考www.regular-expressions.info的风格,更新了本模板的style.css文件,加入了与正则式代码相关的格式:

  • 正则式格式举例:[a-z]+@[a-z]+?\.[a-z]+
  • 匹配格式举例:pig@animals.comchicken@birds.com
  • 普通文本格式举例:这是一些普通文本。hello regex world. pig@animals.com和chicken@birds.com

可以这样使用:在字符串这是一些普通文本。hello regex world. pig@animals.com和chicken@birds.com使用正则式[a-z]+@[a-z]+?\.[a-z]+加以匹配,得到的结果为:pig@animals.comchicken@birds.com

极端粗放型:点号其实是近乎万能的,可以匹配任何字符,限制只在于换行符的匹配上。匹配中文自然不在话下。作为可有可无的背景符,一个.*就能匹配掉包括中文在内的全部字符。这当然是一种极端的情况,因为这样显示不出中文字符串的特性。这不是本文要探讨的。

极端集约型:如果搜索特定文本,例如在一二三四五六七八九十拾佰百千仟万亿中匹配十拾, 直接使用m/十拾/就能搞定。这同样不是本文要探讨的。与\w能匹配英文字母一样,本文想找的是能够匹配所有汉字,而不匹配其它文本的一种简写方式。

普适型型:由于汉字属于Unicode,我们就从unicode里面找。在Unicode Regular Expressions,列出了unicode的许多种表达方式。搜索chinese,找到如下一行:

Writing Systems Blocks
Chinese CJK Unified Ideographs, CJK Unified Ideographs Extension A, CJK Compatibility Ideographs, CJK Compatibility Forms, Enclosed CJK Letters and Months, Small Form Variants, Bopomofo, Bopomofo Extended

关于CJK的含义,是指中日韩统一表意文字(Chinese Japanese Korean Unified Ideographs),可以参考百度释义,或wiki词条。

再查了一下regular expressions,查到其unicode一节有这样的内容:

\p{InCJK_Unified_Ideographs}: U+4E00..U+9FFF

看到这里,我想起了以前写的《匹配用户名的asp正则表达式(包括中文)》一文中,提到的中文匹配为[\u4e00-\u9fa5],原来是有其对应的速记方式的,虽然两者有最后一组字符的差异。看附图可见U+9fa5,最后一个汉字的模样。我爱正则表达式|在RegexBuddy中如何使用正则表达式匹配中文字符|http://iregex.org 此序列的第一位,U+4e00,是汉字

自定义:到目前为止,相当于给汉字找到了官方的身份和说法,使用\p{InCJK_Unified_Ideographs}就能匹配所有的中文字符。我们其实也可以将一些重复出现的东西,封装起来,以备使用。例如,对于阿拉伯数字,我们有\d可以用。对于中文数字一二三四等等,我们有没有办法呢?

$zh_digit=qr/一|二|三|四|五|六|七|八|九|十|零|〇|百|千|万|亿|佰|仟|壹|贰|叁|肆|伍|陆|柒|捌|玖|拾/;
 
$str="人民币五十一万零三百元整。大写:伍拾壹万零三佰元整。";
while($str =~ s/((?:$zh_digit)+)//)
{
	print $1."\n";
}

我爱正则表达式|在RegexBuddy中如何使用正则表达式匹配中文字符|http://iregex.org

其输出结果见附图。

结论

可以使用\p{InCJK_Unified_Ideographs}匹配任意中文字符。在不支持该种标记方式时,也可以使用[\u4e00-\u9fa5]加以匹配。

关于文正则表达式,我觉得尚未穷其奥秘。以前在linux(utf8编码)下,编写scim输入平台的郑码码表时,匹配中文所使用的正则表达式为[\x80-\xff]{3},也能很好地工作。请参阅此文:龙文郑码码表 for scim。其原理我尚不清楚,留待之后有时间研究。如有知情者,也请不吝赐教,先行谢过。

1

饭否私信格式分析

URL

饭否私信分为两种,一种是我收到的私信,一种是我发出的私信。

  • 我收到的私信:http://fanfou.com/privatemsg/p.(1-N)
  • 我发出的私信:http://fanfou.com/privatemsg/sent/p.(1-N)

上面的地址中不含饭否ID;需要cookie验证。

结束标志

通过cookie验证后,可以使用数字获得对应页码的私信内容。什么时候是结束呢?假如您的收件箱有1000条私信,每页显示20条,那么当你您输入http://fanfou.com/privatemsg/p.51时,就得不到任何有效的内容了。作为程序,它是寻找如下标志:

<ol class="wa">\s*</ol>

好友列表

在页面代码中,每页都有一个“向XXX发送私信”的combox列表,条目以昵称+ID组成。如果你的好友很多的话(500+),每条好友(昵称+ID)需要20字节(估算)的话,20*500=10K,大约需要多抓取10K的字节量。

收件箱饭否私信的结构

收到的私信分为两种,一种是有回复信息的(回复原文:…),一种是没有回复的。先从简单的入手,看没有回复的。
所有的私信都在<ol class=”wa”>…</ol>之内,以<li></li>分隔

例如下面这一条,就是一则很规范的私信(与发件人相关的信息都使以正则式表示):

<li>
	<a href="/[^"]+" title="[^"]+" class="avatar"><img src="[^"]+" alt="[^"]+" /></a>
	来自<a href="[^"]+">[^"]+</a><span class="content">没法比较啊,你得说个具体的值,比如100条以下的算少,1000条以上的算多……</span>
	<span class="stamp time" title="2008-05-30 17:25">15 小时前</span> 
	<span class="op">
		<a href="/privatemsg.reply/583520">回复</a> 
		<a href="/privatemsg.del/583520" class="post_act">删除</a></span>
</li>

其中,需要记录的信息有:

  • 发件人名字;
  • 发件人ID;
  • 私信内容;
  • 时间;
  • 私信ID;(便于作删除、回复处理)。

根据以上需求,将上面的私信代码作处理:

<li>
	<a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a>
	来自<a href="[^"]+">[^"]+</a><span class="content">([^<]+)</span>
	<span class="stamp time" title="([^"]+)">[^<]+</span> 
	<span class="op">
		<a href="/privatemsg.reply/(\d+)">回复</a>
		<a href="/privatemsg.del/\d+" class="post_act">删除</a>
	</span>
</li>

从而得到:

$1=fanfou ID;
$2=fanfou name;
$3=private msg;
$4=msg time;
$5=msg ID;

再看一下包含“回复原文”的私信的结构(部分内容已作正则处理):

<li>
	<a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a>
	来自<a href="[^"]+">[^"]+</a><span class="content">([^<]+)</span>
	<span class="stamp time" title="([^"]+)">[^<]+</span> 
	<span class="op">
		<a href="/privatemsg.reply/(\d+)">回复</a> 
		<a href="/privatemsg.del/\d+" class="post_act">删除</a>
	</span>
	<p class="pm-parent">回复原文: 有兴趣就有动力</p>					
</li>

与前者相比,只是多了<p class=”pm-parent”>.*?</p>这一段。这是不足为虑的。只要整体加上?这个强有力的正则符号,就能与上面的代码片段归纳到一起。两者结合合的代码如下:

<li>
	<a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a>
	来自<a href="[^"]+">[^"]+</a><span class="content">([^<]+)</span>
	<span class="stamp time" title="([^"]+)">[^<]+</span> 
	<span class="op">
		<a href="/privatemsg.reply/(\d+)">回复</a> 
		<a href="/privatemsg.del/\d+" class="post_act">删除</a></span>
	(?:<p class="pm-parent">([^<]+)</p>)?
</li>

得到的变量为:

$1=fanfou ID;
$2=fanfou name;
$3=private msg;
$4=msg time;
$5=msg ID;
$6=parent msg;#回复原文。

发件箱饭否私信的结构

抄代码:

<li>
	<a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a>
	发给<a href="[^"]+">[^"]+</a><span class="content">海内的像片是真的。</span>
	<span class="stamp time" title="2008-05-28 20:24">2008-05-28 20:24</span>
	<span class="op"><a href="/privatemsg.del/576827" class="post_act">删除</a></span>
</li>

这与“我收到的私信”的结构完全一致,只是将原来的“来自”改为“发给”而已。

不出意外,带有“回复原文”的“我收到的私信”的结构是这样的:

<li>
	<a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a>
	发给<a href="[^"]+">[^"]+</a><span class="content">在自述部分显示的那个网上。</span>
	<span class="stamp time" title="2008-05-29 17:00">2008-05-29 17:00</span>
	<span class="op"><a href="/privatemsg.del/579918" class="post_act">删除</a></span>
	<p class="pm-parent">回复原文: 我也要试一试。</p>					
</li>

我们从饭否私信代码上得到的信息就这些。遗憾的是,饭否私信中,关于“回复原文”是以明文内容形式出现,而不是以原私信ID的形式出现。后期处理时通过搜索功能解决此问题并非不能,只是如果饭否官方能够再将此功能完善的话,会省整理者不少力气。

饭否的私信源码分析完毕。至于如何读写cookie,如何写代码,如何以数据库的形式来管理下载的数据,是第二阶段的事情了。待我一一实现。

1

精通正则表达式中文第三版到手

今天收到从淘宝上买来的《精通正则表达式》:第3版(中文版),感觉很爽。加上之前购买的精通正则表达式:第2版(影印版),手头就有两本正则书了。之前那边英文版可是“韦编三绝”,被我读得破破烂烂的了。

1

[老贴整理]如何使用正则式从英文句子里提取词根

以前在chinaunix回答过这样一个问题,用到了正则表达式(而且我认为正则式解决此类问题是最合适的。)

学英语的一些例句,每句都有若干词根相同的词,例如 She swears to wear the pearls that appear to be pears. 但是每句的词根都未必相同;我希望把这些包含词根的词都标记出来,请问如何写?

这里说的词根不是原本词根的定义,只是一组字母序列,比如

9. The dust in the industrial zone frustrated the industrious man.

词根是dust或ust

10. The just budget judge just justifies the adjustment of justice.

词根是dust

11. I used to abuse the unusual usage, but now I’m not used to doing so.

词根是use,有变形

12. The lace placed in the palace is replaced first, and displaced later.

词根是lace

13. I paced in the peaceful spacecraft.

词根是pace

14. Sir, your bird stirred my girlfriend’s birthday party.

词根是ir

如果您对此问题感兴趣,请独立思考后再继续阅读本站提供的解决方法。

Read More »

1

Hello regex world!

2008年4月24日,一个普普通通的日子,iregex.org我爱正则式博客低调开张了。使用的平台是wordpress,面向的受众是对正则式有好感或即将有好感的网友。如果您了解并喜欢正则表达式。如果您不了解、不喜欢正则表达式。

什么是正则表达式?如果您在dos中使用过*和?这两个通配符,那你就理解一半了。正则表达式是一整套通配符。它可以像天书(貌似一堆无意义字符的组合),但也可以简洁高雅。它的作用是高效地搜索和替换字符串。与庞大的操作系统相比,它只是一个小零件。但是,这一零件的作用越来越为人所熟知,越来越不可或缺。

本站关注正则表达式。内容包括原创、转载、翻译与正则表达式有关的教程和应用文章。饭友ppip在自述中说,“因此我将坚持多原创、少转载、少翻译的原则,宁可显出自己的愚钝,也绝不借助于他人的头脑表达。”笔者欣赏这种注重提高自身修养的思想境界。但是本站的宗旨在于分享和交流,同时本人技术并非精尖,所以自矜的仅是对正则式的热爱和对技术的痴迷,因此本站会在坚持原创的同时,不排斥转载和翻译本主题的优秀文章,与各位regexers共享。本人也立志从一名regexer成长为regexist,并最终成为regex guru。

除了正则式,本站还会刊登与搜索引擎有关的文章。Google和百度改变了我们的学习、工作和生活。我们从一个信息贫乏时代跨入了信息泛滥时代。如何精准定位所需的信息,想必也是每位网友所关心的。

为了让更多的人了解、应用正则式这一好用的工具,本站还准备开设一个小型论坛(不日开放),专门用来为新手答疑解惑。首先应该声明的是,本人对于正则式只是叶公好龙,愿意组建平台并抛砖引玉,对于新手的问题知无不言言无不尽;更高深的问题,还望诸多大牛现身赐教;在讨论中相互促进,在解惑中得到提高。

本站的域名为iRegEx.org,i可以理解为I,自己,也可以理解为中文的同音字“爱”。RegEx当然是正则式的意思了。.org是非赢利性组织域名,而非.com。旨在聚拢尽可能多的同道中人,而不是攫取尽可能多的利润。当然,如果本站将来做得足够好,流量足够大,亦不排除出售广告位来达到维持平衡的可能性。我正则,爱正则,我爱正则!

做一个与正则式有关的网站,是本人的一个愿望。现在,这是得偿夙愿的开始。我想以一副长联作为本篇开场白的结语。该长联是本人在饭否上写的“自述”:

◆匹配文本,管它字母数字一次多次懒惰贪婪行首行尾,火眼金睛疏不漏
◆替换字串,任尔局部全局单行多行前瞻回顾大写小写,偷梁换柱度陈仓

希望各位网友多支持“我爱正则式”,多订阅,多评论。谢谢大家。

1