正则表达式基础 Regular Expression
正则表达式简介
n 为什么需要正则表达式?
q 文本的复杂处理。
n 正则表达式的优势和用途?
q 一种强大而灵活的文本处理工具;
q 提供了一种紧凑的、动态的方式,能够以一种完全通用的方式来解决各种字符串处理(例如:验证、查找、替换等)问题;
q 大部分语言[d1]、数据库都支持正则表达式。
n 正则表达式定义:
q 正如他的名字一样是描述了一个规则[d2],通过这个规则可以匹配一类字符串。
n 正则表达式的用处:
q 验证给定字符串是否符合指定特征,比如验证是否是合法的邮件地址。
q 用来查找字符串,从一个长的文本中查找符合指定特征的字符串。
q 用来替换,比普通的替换更强大
工具软件RegexBuddy的使用
n 为了提高开发效率,一般都先在工具软件中测试正则表达式,通过测试后,才在程序中使用。
正则表达式规则
n 普通字符
q 字母、数字、汉字、下划线、以及没有特殊定义的标点符号,都是“普通字符”。表达式中的普通字符,在匹配一个字符串的时候,匹配与之相同的一个字符。
n 简单的转义字符
\n | 代表换行符 |
\t | 制表符 |
\\ | 代表\本身 |
\^ ,\$,\.,\(, \) , \{, \} , \? , \+ , \* , \| ,\[, \] | 匹配这些字符本身 |
n 标准字符集合:能够与 ‘多种字符’ 匹配的表达式
q (注意区分大小写,大写是相反的意思)
\d | 任意一个数字,0~9 中的任意一个 |
\w | 任意一个字母或数字或下划线,也就是 A~Z,a~z,0~9,_ 中任意一个 |
\s | 包括空格、制表符、换行符等空白字符的其中任意一个 |
. | 小数点可以匹配除了换行符(\n)以外[d3]的任意一个字符 |
n 自定义字符集合:[ ]方括号匹配方式,能够匹配方括号中任意一个字符
[ab5@] | 匹配 "a" 或 "b" 或 "5" 或 "@" |
[^[d4]abc] | 匹配 "a","b","c" 之外的任意一个字符 |
[f-k] | 匹配 "f"~"k" 之间的任意一个字母 |
[^A-F0-3] | 匹配 "A"~"F","0"~"3" 之外的任意一个字符 |
注意事项:
1. 正则表达式中的特殊符号,如果被包含于中括号中,则失去特殊意义,但 \ [ ] : ^ - 除外。
2. 标准字符集合,除小数点(.)外,如果被包含于中括号中,自定义字符集合将包含该集合。
比如:[\d.\-+],将可以匹配数字,小数点和 + - 符号。(小数点和 + 号失去特殊意义)
n 修饰匹配次数的特殊符号
{n} | 表达式重复n次 |
{m,n} | 表达式至少重复m次,最多重复n次 |
{m,}[d5] | 表达式至少重复m次 |
? | 匹配表达式0次或者1次,相当于 {0,1} |
+ | 表达式至少出现1次,相当于 {1,} |
* | 表达式不出现或出现任意次,相当于 {0,} |
n 匹配次数中的贪婪模式(匹配字符越多越好)
q “{m,n}”, “{m,}”, “?”, “*”, “+”,具体匹配的次数随被匹配的字符串而定。这种重复匹配不定次数的表达式在匹配过程中,总是尽可能多的匹配。
n 匹配次数中的非贪婪模式(匹配字符越少越好)
q 在修饰匹配次数的特殊符号后再加上一个 "?" 号,则可以使匹配次数不定的表达式尽可能少的匹配,使可匹配可不匹配的表达式,尽可能的 "不匹配"。
n 字符边界(本组标记匹配的不是字符而是位置,符合某种条件的位置)
^ | 与字符串开始的地方匹配 |
$ | 与字符串结束的地方匹配 |
\b | 匹配一个单词边界[d6] |
n 选择符和分组
表达式 | 作用 |
| | 左右两边表达式之间 "或" 关系,匹配左边或者右边 |
( ) | (1). 在被修饰匹配次数的时候,括号中的表达式可以作为整体被修饰 (2). 取匹配结果的时候,括号中的表达式匹配到的内容可以被单独得到 (3). 每一对括号会分配一个编号,使用 () 的捕获根据左括号的顺序从 1 开始自动编号。捕获元素编号为零的第一个捕获是由整个正则表达式模式匹配的文本[d7] |
n 反向引用(\nnn)
q 每一对()会分配一个编号,使用 () 的捕获根据左括号的顺序从 1 开始自动编号。
q 通过反向引用,可以对分组已捕获的字符串进行引用。
n 非捕获组(?:xxx)[d8] (基本不用!!!!)
q 与捕获组的区别在于不捕获匹配的文本,仅仅作为分组。其他地方一致。
n 模式修改符 (?ismg[c9])*****(?-ismg) (用的不多,听听就行!)
q 在正则表达式中间,对匹配模式进行修改。
大小写模式修改,比如匹配a,A:
1. [aA]
2. 把整个模式修改为大小写不敏感。
3. (?i)a(?-i)
n 预搜索(零宽断言[d10])
q 判断当前位置的前后字符,是否符合指定的条件,但不匹配前后的字符。是对位置的匹配。
(?=exp) | 断言自身出现的位置的后面能匹配表达式exp |
(?<=exp) | 断言自身出现的位置的前面能匹配表达式exp |
(?!exp) | 断言此位置的后面不能匹配表达式exp |
(?<!exp) | 断言此位置的前面不能匹配表达式exp |
正则表达式的匹配模式
n IGNORECASE 忽略大小写模式
q 匹配时忽略大小写。
q 默认情况下,正则表达式是要区分大小写的。
n SINGLELINE 单行模式
q 整个文本看作一个字符串,只有一个开头,一个结尾。
q 使小数点 "." 可以匹配包含换行符(\n)在内的任意字符。
n MULTILINE 多行模式
q 每行都是一个字符串,都有开头和结尾。
q 在指定了 MULTILINE 之后,如果需要仅匹配字符串开始和结束位置,可以使用 \A 和 \Z
-------------------------------------------------------------------------------------------------------------------------------------------------------
开发中使用正则表达式的流程:
- 分析所要匹配的数据,写出测试用的典型数据
- 在工具软件中进行匹配测试
- 在程序中调用通过测试的正则表达式
课堂练习
n 电话号码验证
q (1)电话号码由数字和"-"构成
q (2)电话号码为7到8位
q (3)如果电话号码中包含有区号,那么区号为三位或四位, 首位是0.
q (4)区号用"-"和其他部分隔开
q (5)移动电话号码为11位
q (6)11位移动电话号码的第一位和第二位为"13“,”15”,”18”
n 电子邮件地址验证
1.用户名:字母、数字、中划线、下划线组成。
2.@
3.网址:字母、数字组成。
4. 小数点:.
5. 组织域名:2-4位字母组成。
6.不区分大小写。[d11]
[a-zA-Z0-9_\-]+@[A-Za-z0-9]+(\.[a-zA-Z]{2,3}){1,2}
使用模式修改符:(?i)[a-z0-9_\-]+@[a-z0-9]+(\.[a-z]{2,3}){1,2}(?-i)
n 抓取163主页上所有图片url地址
使用java编程,将图片地址放入集合中,在打印出来。
所有图片url:http:[\w/\.]+((gif)|(jpg)|(png))
所有超链接:(?<=(<a\s+href="))(http:[\w/.?=&%-]+)(?=(">))
n 将页面:http://www.baidu.com/s?wd=%C6%FB%B3%B5 ,所有的以推广结尾的url抓出来!
开发软件中正则表达式的使用,大大提高开发效率
n 文本编辑器editplus, ultraedit中使用正则表达式
q 很多文本编辑器中并不一定支持所有正则表达式规则,使用前最好查看该软件相关文档
n Eclipse中使用正则表达式
JAVA程序中使用正则表达式
n 相关类位于:java.util.regex包下面
n 类 Pattern:
q 正则表达式的编译表示形式。
q Pattern p = Pattern.compile(r,int); //建立正则表达式,并启用相应模式
n 类 Matcher:
q 通过解释 Pattern 对 character sequence 执行匹配操作的引擎
q Matcher m = p.matcher(str); //匹配str字符串
n 编程中使用正则表达式常见情况:
q 验证表达式是否匹配整个字符串
q 验证表达式是否可以匹配字符串的子字符串
q 返回给定字符串中匹配给定正则表达式所有子字符串
q 替换给定字符串中匹配正则表达式的子字符串
q 根据正则表达式定义规则截取字符串
JAVASCRIPT中使用正则表达式
n Javascript中正则表达式对象的创建
q re = /pattern/[“flags”] à var re1 = /\d{3}/ig;
q re = new RegExp("pattern",["flags"]) à var re2 = new RegExp("\\d{3}","ig");
q Flags可选项常见值:
n g (全文查找出现的所有pattern)
n i (忽略大小写)
n m (多行查找)
n RegExp对象常用方法:
q exec(): 返回的是一个数组。该数组包含了匹配该模式的第一个子字符串以及该子字符串中匹配相关分组的字符串。比如:
var re = new RegExp("(\\d+)([a-z]+)","ig");
var result = re.exec("33ff55tt77uu88yy");
返回的数组为:[33ff,33,ff]
q test(): 返回一个 Boolean 值,它指出在被查找的字符串中是否存在模式匹配的子字符串
n 字符串中常用的使用正则表达式的方式:
q match():使用正则表达式模式对字符串执行查找,并将符合该模式的所有子字符串包含到数组中返回。
var re = new RegExp("(\\d+)([a-z]+)","ig[c12]");
var t = "33ff55tt77uu88yy".match(re);
结果为数组:[33ff,55tt,77uu,88yy]
q search(): 返回与正则表达式查找内容匹配的第一个子字符串的位置
q split(regex):按照指定的模式切割字符串,返回一个数组。
var t = "asdfd33ff55tt77uu88yy".split(/\d+/);
q replace():
var t = "asdfd33ff55tt77uu88yy".replace(/\d+/g[c13],"**");
课后作业
n 阅读分析课堂笔记中常见正则表达式
n 编程题目:
q 抓取http://www.mop.com主页中所有图片的地址. (使用java实现)
q 抓取http://www.mop.com主页中所有的超链接地址. (使用java自己实现)
package test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class SimpleSpider {
/** * 获得url对应的html源码! * @param urlString * @return */ static StringBuilder getHtmlSource(String urlString){ BufferedReader br = null; StringBuilder sb = null; try { URL url = new URL(urlString); br = new BufferedReader(new InputStreamReader(url.openStream())); sb = new StringBuilder(); String temp = ""; while((temp = br.readLine())!=null){ sb.append(temp+"\r\n"); } System.out.println(sb); } catch (Exception e) { e.printStackTrace(); }finally{ try { br.close(); } catch (IOException e) { e.printStackTrace(); } } return sb; }
public static List getSubStrs(String regex,String decStr) { Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(decStr); List<String> list = new ArrayList<String>(); while(m.find()){ //12a345b666cwer list.add(m.group()); } for (String string : list) { System.out.println(string); } return list; }
public static void main(String[] args) { StringBuilder sb = getHtmlSource("http://www.163.com"); getSubStrs("http:[\\w/\\.]+((gif)|(jpg)|(png))",sb.toString());
} } |
q 利用正则表达式的替换,实现去除字符串首尾空格的功能.(分别使用java和javascript实现)
String str = " ab c ".replaceAll("(^\\s+)|(\\s+$)","");
q 实现从HTML页面中抓取有效数据 (搜索引擎中的常用算法,使用正则表达式实现[微软用户14])
将163.com主页中所有有效数据取出。 所谓有效数据指的是显示在页面中的文字。
搜索引擎要抓取页面数据时,不需要关心标签中的文本,显示的文本是用户关心的数据。
如果使用我们原来的算法去写这个需求,当然没有问题。但是算法比较复杂。
在这里,我们也可以通过正则表达式来处理。
q 编程实现自己的网络爬虫(比较麻烦!作为大家晚上的作业!)
1. 可以将指定的html[微软用户15]下载到本地计算机
2. 可以将html中的超链接对应的内容下载到本地计算机中(通过正则表达式得到超链接字符串,通过递归算法下载,可以手动指定递归的level)
针对表单域的验证,封装一个通用的函数:
1. 分析用户名和邮箱验证的共同点:
a) 错误提示文字都会放到某一个元素中。
b) 都不能为空
2. 分析用户名和邮箱验证的不同点:
a) 不为空时,还需要有其他的格式验证。(用户名:长度不能超过30. 邮箱:符合邮箱规格)
封装成的函数:
function validateField(re,fieldObj,errorObj,errorWord,nullWord){
String.prototype.trim = function(){
return this.replace(/^\s+/,"").replace(/\s+$/,"");
var fieldValue = fieldObj.value.trim();
var flag = true;
errorObj.innerHTML="";
if(fieldValue.length==0){
errorObj.innerHTML=nullWord;
flag = false;
}else if(!(re.test(fieldValue))){
errorObj.innerHTML=errorWord;
flag = false;
}
return flag;
}
使用示例:
function check2(frm){
var unameok = validateField(/^.{1,30}$[微软用户17]/,frm.uname,$("unameMsg"),"用户名长度不能超过30","用户名不能为空!");
var emailok = validateField(/^[\w-]+@[a-z0-9]+(\.[a-z]{2,4}){1,2}$/,frm.email,$("emailMsg"),"邮箱格式不正确","邮箱不能为空!");
return unameok&&emailok;
}
<form name="testFrm" action="regex.html" οnsubmit="return check2(this);" >
用户名:<input type=text name=uname /><span id="unameMsg"></span> <br>
邮箱:<input type=text name=email /><span id="emailMsg"></span> <br>
<input type=submit value="注册" /> <br>
</form>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GB18030" /> <title>Untitled Document</title> <script> function check(frm){ /* var uname = frm.userName.value.replace(/(^\s+)|(\s+$)/g,""); var flag = true; document.getElementById("unameMsg").innerHTML=""; document.getElementById("pwdMsg").innerHTML=""; document.getElementById("ageMsg").innerHTML=""; if(uname.length==0){ document.getElementById("unameMsg").innerHTML="用户名不能为空!"; flag = false; }else{ if(!(/^\w+$/.test(uname))){ document.getElementById("unameMsg").innerHTML="用户名必须是:字母、数字、下划线!"; flag = false; } }
var pwd = frm.pwd.value.replace(/(^\s+)|(\s+$)/g,""); if(pwd.length==0){ document.getElementById("pwdMsg").innerHTML="pwd不能为空!"; flag = false; }else{ if(!(/^\w+$/.test(pwd))){ document.getElementById("pwdMsg").innerHTML="pwd必须是:字母、数字、下划线!"; flag = false; } }
var age = frm.age.value.replace(/(^\s+)|(\s+$)/g,""); if(age.length==0){ document.getElementById("ageMsg").innerHTML="age不能为空!"; flag = false; }else{ if(!(/^\d{3}$/.test(age))){ document.getElementById("ageMsg").innerHTML="年龄不能超过3位数或者不是数字"; flag = false; } }
return flag; */ var unameFlag = checkField(frm.userName,$("unameMsg"),/^\w+$/,"用户名不能为空","用户名必须为字母、数字、下划线组成!"); var pwdFlag = checkField(frm.pwd,$("pwdMsg"),/^\w{6,16}$/,"pwd不能为空","用户名必须为字母、数字、下划线组成,并且长度在6-16位之间!"); var ageFlag = checkField(frm.age,$("ageMsg"),/^((\d{1,2})|(1[0123]\d))$/,"age不能为空","年龄不能高于139!"); return unameFlag&&pwdFlag&&ageFlag; }
function $(a){ return document.getElementById(a); }
function checkField(fieldObj,msgObj,re,nullMsg,errorMsg){ msgObj.innerHTML = ""; var v = fieldObj.value.replace(/(^\s+)|(\s+$)/g,""); var flag = true; if(v.length==0){ msgObj.innerHTML=nullMsg; flag = false; }else{ if(!(re.test(v))){ msgObj.innerHTML=errorMsg; flag = false; } }
return flag; }
</script> </head> <body> <form action="formValidate.htm" name=testFrm οnsubmit="return check(this);" > 用户名:<input type=text name=userName /> <span id=unameMsg ></span> <br> 密码:<input type=password name=pwd /> <span id=pwdMsg></span> <br> 年龄:<input type=text name=age /> <span id=ageMsg></span> <br> <input type=submit value=注册 /> </form> </body> </html> |
[d2]学习正则表达式很大程度上就是学习正则表达式的语法规则。
[d3]在单行模式下面可以匹配包含换行符在内的任意字符。
[d4]写到方括号内时表示取非的含义。
[d5]没有{,5}这样的写法。
[d6]精确地说,\b匹配这样一个位置:前面的字符和后面的字符不全是\w.
[d7]讲到java编程时给大家详细介绍。
[d10]这个位置应该符合某个条件。
i. [c12]注意:这种用法,正则表达式对象的模式必须为global模式。
[c13]指定为global模式,否则只替换第一个子字符串
[微软用户14]这么做不专业!仅仅作为正则表达式的一个练习作业。下周讲搜索引擎会有专业的做法。
[微软用户15]文档对应的css/js/img文件,可以下载到对应的文件夹. 也可以不做处理. 只下载html即可.
[微软用户16]增加去空格处理更合理!
[微软用户17]去验证整个表达式较好!