Delphi正则表达式验证操作

xl040301

贡献于2011-03-01

字数:15566 关键词: Delphi开发 Delphi

一、工具选择 在 Delphi 中使用正则表达式, 目前 PerlRegEx 应该是首选, 准备彻底而细致地研究它. 官方网站: http://www.regular-expressions.info/delphi.html 直接下载: http://www.regular-expressions.info/download/TPerlRegEx.zip 二、安装方法: 1、先把解压的 TPerlRegEx 文件夹放一个合适的地方, 我放在了 Delphi 的 Imports 目录中. 2、目前最新 For Win32 的版本是对 Delphi 2006 的, 2007 也能用.      打开 PerlRegExD2006.dpk, 提示缺少资源文件, 没关系;      在 Project Manager 窗口中的 PerlRegExD2006.bpl 上点击右键, 执行 Install;      这时在 Tool Palette 的列表中已经有了 TPerlRegEx, 在 JGsoft 组. 3、Tools -> Options -> Environment Options -> Delphi Options -> Library-Win32 -> Library path ->      添加路径: ...\Imports\TPerlRegEx 4、可以使用了! 直接 uses PerlRegEx 或从 Tool Palette 添加都可以.      如果不喜欢 Tool Palette 的添加方式可以省略第二步. 三、使用方法 PerlRegEx提供了TPerlRegEx类。主要用法是: RegEx : TPerlRegEx; .... RegEx := TPerlRegEx; try    RegEx.Subject := '要匹配的正文';    RegEx.RegEx := '正则表达式';    if RegEx.Match then .... finally    RegEx.free end; 如果要多次匹配并做一些处理,可以: Matched : boolean; .... RegEx.Match; while RegEx.FoundMatch do begin    ....    RegEx.MatchAgain; end; 如果要替换匹配到的内容,可以 RegEx.Subject := '要匹配的正文'; RegEx.RegEx := '正则表达式'; RegEx.Replace := '替换的字符串' if RegEx.Match then RegEx.ReplaceAll;   //结果在RegEx.subject 或者 if RegEx.Match then Result := RegEx.Replacement; 匹配到的字符串放在RegEx.MatchedExpression中,长度在RegEx.MatchedExpressionLength中,上一次匹配的结束位置在RegEx.Stop中 匹配到的子串放在RegEx.SubExpressions中,子串个数在RegEx.SubExpressionCount中。 如果正则式很复杂而且常用,可创建一个生存期相对长的TPerlRegEx实例.设置好RegEx属性后,使用.Study方法对正则表达式进行预处理.据帮助文档说,文档资料会大大提高效率. 详情可参考文档。有一点文档上没有提到(又或者我看漏了),在第一次匹配之后,如果没有重新赋值subject,下一次匹配无论用Match或者MatchAgain,都是从上次的结束位置开始。所以如果要重新开始匹配,应先把RegEx.Start := 0; 四、正则单元 程序代码 unit UnitRegEx; interface uses    PerlRegEx; function CheckEmail(EmailAddr: string): Boolean; function CheckStrOrNumber(Str: string): Boolean; var    PerlRegEx: TPerlRegEx; implementation //Email电子邮箱检测 function CheckEmail(EmailAddr: string): Boolean; begin    PerlRegEx := TPerlRegEx.Create(nil);    PerlRegEx.Subject := EmailAddr;    PerlRegEx.RegEx := '\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*';    Result := PerlRegEx.Match; end; //字符、数字检测 function CheckStrOrNumber(Str: string): Boolean; begin    PerlRegEx := TPerlRegEx.Create(nil);    PerlRegEx.Subject := Str;    PerlRegEx.RegEx := '^[A-Za-z0-9]+$';    Result := PerlRegEx.Match; end; end. 五、使用实例 程序代码 procedure TFrmRegister.btnNextClick(Sender: TObject); begin    try      if not CheckStrOrNumber(edtUser.Text) then      begin        Application.MessageBox(PChar('请您正确输入用户名。'), '系统提示', 64);        edtUser.SetFocus;        edtUser.SelText;        Exit;      end      else if not CheckEmail(edtEmail.Text) then      begin        Application.MessageBox(PChar('请您正确输入常用的电子邮箱。'), '系统提示', 64);        edtEmail.SetFocus;        edtEmail.SelText;        Exit;      end;    except      on E: Exception do        Application.MessageBox(PChar(E.Message), '系统提示', 64)    end; end; 六、常用正则表式 正则表达式用于字符串处理、表单验证等场合,实用高效。现将一些常用的表达式收集于此,以备不时之需。 匹配中文字符的正则表达式: [\u4e00-\u9fa5] 评注:匹配中文还真是个头疼的事,有了这个表达式就好办了 匹配双字节字符(包括汉字在内):[^\x00-\xff] 评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1) 匹配空白行的正则表达式:\n\s*\r 评注:可以用来删除空白行 匹配HTML标记的正则表达式:<(\S*?)[^>]*>.*?|<.*? /> 评注:网上流传的版本太糟糕,上面这个也仅仅能匹配部分,对于复杂的嵌套标记依旧无能为力 匹配首尾空白字符的正则表达式:^\s*|\s*$ 评注:可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式 匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)* 评注:表单验证时很实用 匹配网址URL的正则表达式:[a-zA-z]+://[^\s]* 评注:网上流传的版本功能很有限,上面这个基本可以满足需求 匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$ 评注:表单验证时很实用 匹配国内电话号码:\d{3}-\d{8}|\d{4}-\d{7} 评注:匹配形式如 0511-4405222 或 021-87888822 匹配腾讯QQ号:[1-9][0-9]{4,} 评注:腾讯QQ号从10000开始 匹配中国邮政编码:[1-9]\d{5}(?!\d) 评注:中国邮政编码为6位数字 匹配身份证:\d{15}|\d{18} 评注:中国的身份证为15位或18位 匹配ip地址:\d+\.\d+\.\d+\.\d+ 评注:提取ip地址时有用。 匹配特定数字: ^[1-9]\d*$    //匹配正整数 ^-[1-9]\d*$   //匹配负整数 ^-?[1-9]\d*$   //匹配整数 ^[1-9]\d*|0$  //匹配非负整数(正整数 + 0) ^-[1-9]\d*|0$   //匹配非正整数(负整数 + 0) ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$   //匹配正浮点数 ^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$  //匹配负浮点数 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$  //匹配浮点数 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$   //匹配非负浮点数(正浮点数 + 0) ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$  //匹配非正浮点数(负浮点数 + 0) 评注:处理大量数据时有用,具体应用时注意修正。 匹配特定字符串: ^[A-Za-z]+$  //匹配由26个英文字母组成的字符串 ^[A-Z]+$  //匹配由26个英文字母的大写组成的字符串 ^[a-z]+$  //匹配由26个英文字母的小写组成的字符串 ^[A-Za-z0-9]+$  //匹配由数字和26个英文字母组成的字符串 ^\w+$  //匹配由数字、26个英文字母或者下划线组成的字符串 评注:最基本也是最常用的一些表达式。 -------------------------------------- 试验:删除http所有标签 var reg: TPerlRegEx; str:string; begin str:=memo1.Text; //有<>等内容 reg := TPerlRegEx.Create(nil); reg.Subject := memo1.Text; reg.RegEx   := '\<[^>]+()\>'; while reg.MatchAgain do begin     str:=StringReplace(str,reg.SubExpressions[0],'',[rfReplaceAll]); //删除HTTP标签 end; ShowMessage(str); FreeAndNil(reg); end; 结果:正确! 第二内容 //查找是否存在 var reg: TPerlRegEx; begin reg := TPerlRegEx.Create(nil); reg.Subject := 'CodeGear Delphi 2007 for Win32'; reg.RegEx   := '\d'; if reg.Match then     ShowMessage('找到了') else     ShowMessage('没找到'); FreeAndNil(reg); end; -------------------------------------------------------------------------------- //查找是否存在(方法2) var reg: TPerlRegEx; begin reg := TPerlRegEx.Create(nil); reg.Subject := 'CodeGear Delphi 2007 for Win32'; reg.RegEx   := '\d'; reg.Match; //执行查找 if reg.FoundMatch then //布尔变量 FoundMatch 会告诉我们查找有没有结果     ShowMessage('找到了') else     ShowMessage('没找到'); FreeAndNil(reg); e nd; -------------------------------------------------------------------------------- //显示找到的第一个 var reg: TPerlRegEx; begin reg := TPerlRegEx.Create(nil); reg.Subject := 'CodeGear Delphi 2007 for Win32'; reg.RegEx   := '\d'; if reg.Match then     ShowMessage(reg.MatchedExpression) //2 else     ShowMessage('没找到'); FreeAndNil(reg); end; -------------------------------------------------------------------------------- //分别显示找到的每一个和总数 var reg: TPerlRegEx; num: Integer; //用 num 来计数 begin reg := TPerlRegEx.Create(nil); reg.Subject := 'CodeGear Delphi 2007 for Win32'; reg.RegEx   := '\d'; num := 0; while reg.MatchAgain do //MatchAgain 是下一个 begin     ShowMessage(reg.MatchedExpression); //将分别显示: 2 0 0 7 3 2     Inc(num); end;     ShowMessage(IntToStr(num)); //6 FreeAndNil(reg); end; -------------------------------------------------------------------------------- //分别显示找到的每一个和总数(另一种写法) var reg: TPerlRegEx; num: Integer; //用 num 来计数 begin reg := TPerlRegEx.Create(nil); reg.Subject := 'CodeGear Delphi 2007 for Win32'; reg.RegEx   := '\d'; num := 0; if reg.Match then begin     repeat       ShowMessage(reg.MatchedExpression); //将分别显示: 2 0 0 7 3 2       Inc(num);     until (not reg.MatchAgain); end;     ShowMessage(IntToStr(num)); //6 FreeAndNil(reg); end; -------------------------------------------------------------------------------- //目标字符串的位置与长度 var reg: TPerlRegEx; begin reg := TPerlRegEx.Create(nil); reg.Subject := 'CodeGear Delphi 2007 for Win32'; reg.RegEx   := 'Delphi'; while reg.MatchAgain do //很明显: 本例只能找到一个结果 begin     ShowMessage(reg.MatchedExpression); //找到的字符串: 2007     ShowMessage(IntToStr(reg.MatchedExpressionOffset)); //它所在的位置: 10     ShowMessage(IntToStr(reg.MatchedExpressionLength)); //它的长度: 6 end; FreeAndNil(reg); end; // MatchedExpression 与 SubExpressions[0] var reg: TPerlRegEx; begin reg := TPerlRegEx.Create(nil); reg.Subject := 'CodeGear Delphi 2007'; reg.RegEx   := 'Delphi'; while reg.MatchAgain do begin     ShowMessage(reg.MatchedExpression); //Delphi; 这是匹配到的内容     ShowMessage(reg.SubExpressions[0]); //Delphi; 也可以这样显示匹配到的内容 end; { SubExpressions 是一个数组: SubExpressions[1] 储存第 1 个表达式匹配的内容; SubExpressions[2] 储存第 2 个表达式匹配的内容; SubExpressions[n] 储存第 n 个表达式匹配的内容; SubExpressions[0] 储存整个表达式匹配的内容; MatchedExpression 表示的不过是 SubExpressions[0]. } FreeAndNil(reg); end; -------------------------------------------------------------------------------- //提取子表达式匹配到的内容 var reg: TPerlRegEx; begin reg := TPerlRegEx.Create(nil); reg.Subject := 'abc A1111 BB222 CCC33 DDDD4'; reg.RegEx   := '\b([A-D]+)([1-4]+)\b'; //这个表达式有两个子表达式构成 while reg.MatchAgain do begin     ShowMessage(reg.SubExpressions[0]); //将分别显示: A1111 BB222 CCC33 DDDD4     ShowMessage(reg.SubExpressions[1]); //将分别显示: A BB CCC DDDD     ShowMessage(reg.SubExpressions[2]); //将分别显示: 1111 222 33 4     {另外:       reg.SubExpressionCount      是子表达式的个数;       reg.SubExpressionLengths[n] 是第 n 个表达式返回的字符串的长度;       reg.SubExpressionOffsets[n] 是第 n 个表达式返回的字符串在源字符串中的位置     } end; FreeAndNil(reg); end; -------------------------------------------------------------------------------- //子表达式不能超过 MAX_SUBEXPRESSIONS = 99 个, MAX_SUBEXPRESSIONS 是 TPerlRegEx 的内置常数. -------------------------------------------------------------------------------- //设定搜索范围: Start、Stop var reg: TPerlRegEx; begin reg := TPerlRegEx.Create(nil); reg.Subject := 'ababab'; reg.RegEx   := 'ab'; reg.Replacement := '◆'; reg.Start := 1; reg.Stop := 2; while reg.MatchAgain do begin     reg.Replace; end; ShowMessage(reg.Subject); //返回: ◆abab reg.Subject := 'ababab'; reg.Start := 3; reg.Stop := 4; while reg.MatchAgain do begin     reg.Replace; end; ShowMessage(reg.Subject); //返回: ab◆ab reg.Subject := 'ababab'; reg.Start := 5; reg.Stop := 6; while reg.MatchAgain do begin     reg.Replace; end; ShowMessage(reg.Subject); //返回: abab◆ FreeAndNil(reg); end; // Replace var reg: TPerlRegEx; begin reg := TPerlRegEx.Create(nil); reg.RegEx   := 'ab'; reg.Replacement := '◆'; reg.Subject := 'ababab'; reg.ReplaceAll; ShowMessage(reg.Subject); //返回: ◆◆◆ reg.Subject := 'ababab'; //下面四行程序, 相当于 reg.ReplaceAll; while reg.MatchAgain do begin     reg.Replace; end; ShowMessage(reg.Subject); //返回: ◆◆◆ FreeAndNil(reg); end; { ReplaceAll 函数返回的是 Boolean; Replace 函数返回的是 Replacement 的值, 当然是不能赋值的, 它仅仅是返回值. } // Compile、Study var reg: TPerlRegEx; begin reg := TPerlRegEx.Create(nil); reg.RegEx   := 'ab'; reg.Options := [preCaseLess]; reg.Compile; {编译表达式} reg.Study; {Study 方法会检查是否编译, 如果没有编译则执行 Compile} reg.Replacement := '◆'; reg.Subject := 'abAbaB'; reg.ReplaceAll; ShowMessage(reg.Subject); {返回: ◆◆◆} FreeAndNil(reg); end; { 编译表达式, 会加快执行速度、降低启动速度; 如果表达式比较复杂而又多次执行, 应该先编译; 编译内容包括表达式选项. } // EscapeRegExChars 函数可以自动为特殊字符加转义符号 \ var reg: TPerlRegEx; begin reg := TPerlRegEx.Create(nil); reg.Subject := 'C++Builer'; reg.RegEx   := reg.EscapeRegExChars('C+') + '{2}'; {相当于 'C\+{2}'} reg.Replacement := '◆'; reg.ReplaceAll; ShowMessage(reg.Subject); {返回: ◆Builer} FreeAndNil(reg); end; //字符串分割: Split var reg: TPerlRegEx; List: TStrings; begin List := TStringList.Create; reg := TPerlRegEx.Create(nil); reg.Subject := 'aaa,bbb,ccc,ddd'; reg.RegEx   := ','; {这里可是运行相当复杂的分割符啊} reg.Split(List,MaxInt); {第一个参数读入的是 Subject; 第二个参数是分成多少份} { 输入一个最大整数, 表示能分多少就分多少} ShowMessage(List.Text); {返回:     aaa     bbb     ccc     ddd } FreeAndNil(reg); List.Free; end; 第三内容 比较好的 RegEx 类库有 TRegExpr ( http://www.regexpstudio.com/ )与PerlRegEx ( http://www.regular-expressions.info/ )。 TRegExpr是俄国人做的RE类库,应该说是目前国内最主流的免费RegEx类库了。纯DELPHI写成,支持中文,可以选择安装为开发环境控件,也可直接作为类库单元使用(只有一个主类,一个pas文件)。美中不足是自从2004年后就没有更新了,版本一直是0.9xxx,就是不上1。而且不支持Lookaround语法(前瞻与回溯功能) PerlRegEx底层是用C的类库,完全符合PCRE标准(兼容Perl的正则表达式)。文件结构比TRegExpr复杂一点,包括一个放底层obj文件的子目录和两个接口.pas文件,实际使用时只需要向项目中添加一个单元(当然也可以注册成为控件)。之前的版本据说对中文支持不够,最新版本我在中文环境下用倒没遇到什么问题。说明文档也声称支持Unicode。 现在我是常备两个这两个类库,但主要还是用PerlRegEx。除了迷信最后更新日期与C的执行效率外。还看中了RegexRubby这个基于同一套C类库的RegEx编写工具,以及PerlRegEx提供了一个study方法,声称可以对正则式做点前期编译,提高执行效率。 ========================================================================= 二 使用方法: 解压了PerlRegEx包后,如果不想注册控件,除了PerlRegEx.pas、CHelpers.pas和PCRE目录外,其他的东西可以建个隐藏目录搁置起来(没认真阅读用户协议,不知道能不能随便删。。。)。维持这两个文件与PCRE的目录结构不要变。使用时只需要把PerlRegEx.pas添加进项目,在单元中uses PerlRegEx就可以了。 PerlRegEx提供了TPerlRegEx类。主要用法是: RegEx : TPerlRegEx; .... RegEx := TPerlRegEx; try RegEx.Subject := '要匹配的正文'; RegEx.RegEx := '正则表达式'; if RegEx.Match then .... finally RegEx.free end; 如果要多次匹配并做一些处理,可以: Matched : boolean; .... RegEx.Match; while RegEx.FoundMatch do begin .... RegEx.MatchAgain; end; 如果要替换匹配到的内容,可以 RegEx.Subject := '要匹配的正文'; RegEx.RegEx := '正则表达式'; RegEx.Replace := '替换的字符串' if RegEx.Match then RegEx.ReplaceAll; //结果在RegEx.subject 或者 if RegEx.Match then Result := RegEx.Replacement; 匹配到的字符串放在RegEx.MatchedExpression中,长度在RegEx.MatchedExpressionLength中,上一次匹配的结束位置在RegEx.Stop中 匹配到的子串放在RegEx.SubExpressions中,子串个数在RegEx.SubExpressionCount中。 如果正则式很复杂而且常用,可创建一个生存期相对长的TPerlRegEx实例.设置好RegEx属性后,使用.Study方法对正则表达式进行预处理.据帮助文档说,文档资料会大大提高效率. 详情可参考文档。有一点文档上没有提到(又或者我看漏了),在第一次匹配之后,如果没有重新赋值subject,下一次匹配无论用Match或者MatchAgain,都是从上次的结束位置开始。所以如果要重新开始匹配,应先把RegEx.Start := 0; ========================================================================== 三. 使用和编写正则表达式要点 使用正则表达式,通常是用作三种用途:校验字符串,提取信息,处理字符串. 当用作校验时,通常是对正文整体校验,例如通常是判断正文是否正确的邮件地址,而不是判断正文是否含有正确的邮件地址.因此应在正则表达式的两端加上行开始锚点^与行结束锚点$.如果待校验的文字允许两端有空格,则应该在锚点前后用' *'或'\s*'(允许空格与TAB)匹配进去. 设计正则表达式的要点在于分段.对要匹配的内容分好段,就能够容易地各个击破.通常在要匹配的文字中会有一些分段的提示,例如逻辑上的单位、重复出现的模式或者不能连续重复出现的字符(串)。 以设计校验输入数字的正则条件为例,可以先列出符合条件的情况: 1234 / 12.34 / -12.34 / 12.3e4 / 12.3e-4 / .12E-34 可以看出,逻辑上的单位有:符号,整数部分,小数点,小数部分,e(或E),指数符号,指数部分 技术上的分段标志有: 符号:在开始与E后面各可能出现一次 小数点:只能出现一次,若出现,其后必须有小数部分。 e:只能出现一次,若出现,其后必须有指数部分。 所有“若出现,其后必须有。。。”的都可以考虑分为一组。可得初步方案: [+\-]?\d*(\.\d+)?([Ee][+\-]*\d+)? 但这个设计有问题,前半段的 \d*(\.\d+)? 是可以匹配空串的,而需求是如果有整数部分,则小数部分可选。如果无整数部分,则必须有小数部分,直观的做法是改为(\d+(\.\d+)?|\.\d+)。再认真观察一下,可以发现这个选择式无论任何情况,都是以\d+结尾,而我们实际上并不关心这个\d+是匹配到整数部分还是小数部分,至于前面的小数点与整数部分都是可选的。因此,这部分可以改写为 \d*\.?\d+ 所以最终的校验式是:^ *[+\-]?\d*\.?\d+([Ee][+\-]*\d+)? *$ 使用正则表达式提取信息是一个难点,但也是体现正则表达式强大实力的一个方面。提取信息的正则表达式必须要考虑四个方面:不误判(应该有一定语法检验能力),不漏判,子串能匹配到正确位置。一些结构复杂或具有循环结构的正文,可能需要多次处理或使用开发语言的循环结构来辅助提取。具体技巧我现在还觉得比较模糊,以下仅举几个例子: 1. 查找并分析 XX1>XX2,<,=,!,空格,TAB符号的任何字符串,式子两端与元素之间允许有空格或TAB 由于这个不等式可能在上下文中,我们需要先在正文中把合语法的不等式隔离出来,否则下面的循环部分就会匹配到下一条不等式的部分。在这个例子中还算简单,找到 '\b[^<>=!\s]+((不等符号)[^<>=!\s]+)+\b' 就可以了(其中不等符号在下面解释)。但这样只能匹配到整个式子而不能分别提取式子中的子串信息,当出现(...)+时,对应子串内容是只是最后一个匹配到的串。 对每个匹配到的结果,因此这里需要分开两次提取,第二次需要使用循环来辅助。 先取最开始的子串,这个很容易,直接 '^\s*([^<>=\s]+)'就可以了,注意^ *是为了去掉开头多余的空格与TAB。真正的XX1在匹配到的子串1中。 接着开始分析 '((不等符号)[^<>=!\s]+)+' 部分。整个(...)+结构需要在外部用开发语言的循环来逐次提取。先列出合法的不等符号:>=,>,=,<=,<,<>,==,!>,!<,!>=,!<=,!=。因此不等符号部分应该是(!?>=?|!?<=?|!?==?|<>)。 因此,要分析篇正文,就需要: 1)A匹配 '\b[^<>=!\s]+(\s*(!?>=?|!?<=?|!?==?|<>)\s*[^<>=!\s]+)+\b' 找出不等式。 2) 对每个A匹配结果, B匹配'^\s*([^<>=!\s])',提取子串1记录为变量名称。 3) 接着B匹配'\s*(!?>=?|!?<=?|!?==?|<>)\s*([^<>=!\s]+)',提取子串1记录为符号,子串2记录为变量名称。 4) 从3)开始循环直到找不到B匹配结果 5)从1)开始循环直到找不到A匹配结果 2. 提取电话号码 这是不久前帮朋友做的一个小程序。事情是这样的:他的公司需要撒网式找澳大利亚酿酒公司合作伙伴,他的任务就是把网上查到的酿酒公司的联系方式记录入库。记录联系方式的数据库是把公司名称,地址,电话号码,传真号码,邮箱等信息分开不同字段储存的。于是他必须用鼠标在网页准确选下各种信息然后粘贴到数据表中,不但工作效率低,而且据说由于鼠标精确动作太多,手腕酸得不得了。于是找我帮忙写个小程序,需求是他可以把联系方式部分整个复制下来,我的程序自动提取出有关信息。以下是提取电话号码信息功能的设计过程: 我看了一下联系方式的可能情况,发现有时里面会包括多个电话号码,而朋友的数据库只记录一个号码。因此我决定把他选定文本中所有电话号码都提取出来,列在一个ComboBox中让他选择。在正文中标记为“Phone”或"h"或""的优先列在前面。 电话号码的写法五花八门,先考虑电话的标记,一般有以下几种: Phone, Phone:, (Phone), P, P-, PH 等等 因此可把匹配标记的正则表达式设计为 '(?-i)(?:\(?Phone|Ph?)[-:]?\s*\)?)'。这种写法有个缺陷是无法保证两边括号能匹配(例如能匹配到'(Phone:')。但在这里的目的并不是校验,括号不匹配并不影响我提取电话号码,只求简单写成这样就够了,否则就要写成:'(?hone|Ph?)[-:]?|\((?hone|Ph?)[-:]?\s*\))' ,麻烦得多。 然后考虑电话号码本身,一个完整的固定电话号码可能是这样的: +61 2 1234 5678 , (61 2) 1234 5678 , (61-)2-12345678, 61 (0) 2 12345678 而也可能省略国家代码(+61)或洲区号(02)简写成 02 1234 5678 , 1234 5678, 2-1234-5678 也有可能不按主号码四字一断的做法,写成 02 123 456 78 等等 构建正则表达式的过程如下: 匹配国家代号:(?:[+( ]*61[-) ]*)? 可以看出,这个式子如果用来校验是不合格的,它能匹配到'(++61-)-'这样的正文。但我在这里是为了提取公司网页上的信息,公司不会在自己的联系方法信息里放入这样的乱码。提取信息时,根据需求,在不误判的前提下可以假定输入信息不会出现太离谱的错误。 匹配区号:(?:\(? *0?\)? *(\d) *[-)]? *)? 同上,这个式子也可能匹配到不合法的正文。这里如果把区号与主体号码合并处理会简单很多。但我在这里想把区号提取出来,作为判断省份的一个依据。 匹配电话号码主体:(?:\d{5,}(?:[ -]\d+)*|\d{1,4}(?:[ -]\d+)+) 如果仅考虑匹配电话号码,用\d*(?: [ -]\d+)*就行了。但澳大利亚的邮政编码刚好是四位数字,而且地址中的信箱号有可能是3到4位数字。因此这里用了个麻烦的写法,如果连续数字小于5位,则后面必须跟一个连接号或空格,然后再跟数字,才能匹配到。 因此优先选取的号码的正则表达式是:(?-i)(?:\(?Phone|Ph?)[-:]?\s*\)?)((?:[+( ]*61[-) ]*)?(?:\(? *0?\)? *(\d) ?[-)]?\s*)?(?:\d{5,}(?:[ -]\d+)*|\d{1,4}(?:[ -]\d+)+)) 电话号码在子串1,区号在子串2 第一轮扫描正文,一边找到匹配,一边把其替换成''。完成后,就可以用((?:[+( ]*61[-) ]*)?(?:\(? *0?\)? *(\d) ?[-)]?\s*)?(?:\d{5,}(?:[ -]\d+)*|\d{1,4}(?:[ -]\d+)+)) (去掉了匹配电话标志部分)对替换后的正文,找出所有没有电话标志,但符合格式的子串 01 常用正则表达式 02   03   04  正则表达式用于字符串处理、表单验证等场合,实用高效。现将一些常用的表达式收集于此,以备不时之需。 05   06   07 匹配中文字符的正则表达式: [\u4e00-\u9fa5] 08 评注:匹配中文还真是个头疼的事,有了这个表达式就好办了 09   10 匹配双字节字符(包括汉字在内):[^\x00-\xff] 11 评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1) 12   13 匹配空白行的正则表达式:\n\s*\r 14 评注:可以用来删除空白行 15   16 匹配HTML标记的正则表达式:<(\S*?)[^>]*>.*?|<.*? /> 17 评注:网上流传的版本太糟糕,上面这个也仅仅能匹配部分,对于复杂的嵌套标记依旧无能为力 18   19 匹配首尾空白字符的正则表达式:^\s*|\s*$ 20 评注:可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式 21   22 匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)* 23 评注:表单验证时很实用 24   25 匹配网址URL的正则表达式:[a-zA-z]+://[^\s]* 26 评注:网上流传的版本功能很有限,上面这个基本可以满足需求 27   28 匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$ 29 评注:表单验证时很实用 30   31 匹配国内电话号码:\d{3}-\d{8}|\d{4}-\d{7} 32 评注:匹配形式如 0511-4405222 或 021-87888822 33   34 匹配腾讯QQ号:[1-9][0-9]{4,} 35 评注:腾讯QQ号从10000开始 36   37 匹配中国邮政编码:[1-9]\d{5}(?!\d) 38 评注:中国邮政编码为6位数字 39   40 匹配身份证:\d{15}|\d{18} 41 评注:中国的身份证为15位或18位 42   43 匹配ip地址:\d+\.\d+\.\d+\.\d+ 44 评注:提取ip地址时有用 45   46 匹配特定数字: 47 ^[1-9]\d*$    //匹配正整数 48 ^-[1-9]\d*$   //匹配负整数 49 ^-?[1-9]\d*$   //匹配整数 50 ^[1-9]\d*|0$  //匹配非负整数(正整数 + 0) 51 ^-[1-9]\d*|0$   //匹配非正整数(负整数 + 0) 52 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$   //匹配正浮点数 53 ^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$  //匹配负浮点数 54 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$  //匹配浮点数 55 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$   //匹配非负浮点数(正浮点数 + 0) 56 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$  //匹配非正浮点数(负浮点数 + 0) 57 评注:处理大量数据时有用,具体应用时注意修正 58   59 匹配特定字符串: 60 ^[A-Za-z]+$  //匹配由26个英文字母组成的字符串 61 ^[A-Z]+$  //匹配由26个英文字母的大写组成的字符串 62 ^[a-z]+$  //匹配由26个英文字母的小写组成的字符串 63 ^[A-Za-z0-9]+$  //匹配由数字和26个英文字母组成的字符串 64 ^\w+$  //匹配由数字、26个英文字母或者下划线组成的字符串

下载文档,方便阅读与编辑

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 15 金币 [ 分享文档获得金币 ]
1 人已下载

下载文档

相关文档