附常用工具:
在线正则测试:http://tool.oschina.net/regex/
正则是什么
正则表达式是为了对字符串进行有效 数据提取 以及 匹配 的一种机制,字符串在匹配的过程中将会从第一个位置开始匹配,然后从左往右进行依次匹配,每尝试匹配一次,就会把控制权交由下一个位置,直到匹配结束。
正则表达式是由 普通字符(例如字符 a 到 z)以及 特殊字符(称为元字符)组成的文字模式。该模式描述在查找文字主体时待匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
正则的诞生
正则表达式的“祖先”可以一直上溯至对人类神经系统如何工作的早期研究。Warren McCulloch 和 Walter Pitts 这两位神经生理学家研究出一种数学方式来描述这些神经网络。
1956 年, 一位叫 Stephen Kleene 的美国数学家在 McCulloch 和 Pitts 早期工作的基础上,发表了一篇标题为「神经网事件的表示法」的论文,引入了正则表达式的概念。
正则表达式就是用来描述他称为“正则集的代数”的表达式,因此采用“正则表达式”这个术语。
随后,人们发现可以将这一工作应用于使用Ken Thompson 的计算搜索算法的一些早期研究,Ken Thompson是Unix 的主要发明人。正则表达式的第一个实用应用程序就是 Unix 中的qed 编辑器。从那时起直至现在正则表达式都是基于文本的编辑器和搜索工具中的一个重要部分。具有完整语法的正则表达式使用在字符的格式匹配方面上,后来被应用到熔融信息技术领域。自从那时起,正则表达式经过几个时期的发展,现在的标准已经被ISO(国际标准组织)批准和被Open Group组织认定。
匹配规则
下面将正则中的一些基本的匹配规则列出来如下表所示:
要点
贪与不贪
举个例子,假设有以下这段html字符,我想拿到a标签中的内容:
南京长江大桥哈哈南京市长江大桥
然后我写了这样一个正则: (.)*
在线测试的结果如下:
这个结果与我们的预期不符,正常我应该得到两个匹配的结果才对,但是现在却只匹配到一个结果。
现在把刚刚的正则改成这样: (.)*?
在线测试的结果如下:
贪 说的是正则在不约束的情况下会继续自动向右进行匹配,直到匹配结束,只要匹配的数据与正则的最后一个值匹配就算是匹配到了。
不贪 说的是只要匹配到就结束,不继续向右进行匹配了。
问号 ? 就解决了贪婪的问题,使得问号前面的字符匹配到之后就结束,但是并不是把 ?放在哪里都可以解决贪婪的,在正则里,有一些属于贪婪模式量词,比如以下这些:
{m,n}
{m,}
?
*
+
断言与零宽
在java中我们知道 断言 可以用来声明一个应该为 true 的事实,只有当断言为真时才会继续进行后续的操作。
在正则中也有 断言 的概念,但是在正则中除了 断言 还有 零宽 的概念。
断言:
通俗点将断言就是 “我断定某某情况是真的” ,而正则中的断言,就是说正则可以断定在 指定的内容 的 前面 或 后面 会出现满足指定规则的内容。比如 “aa1bb2cc3”,正则可以用断言找出 bb2 前面有 aa1,也可以找出 bb2 后面有 cc3。
零宽:
零宽就是没有宽度,在正则中,断言只是匹配位置,不占字符,也就是说,匹配结果里是不会返回断言本身的。
断言一共有四种情况:
让我们来举个例子来说明吧,假设我们现在拿到了某个网页的html,里面有个阅读数的标签:
阅读数:1024
现在我们要获取到这个阅读数,该怎么办呢?
如果用正向先行断言来匹配的话,可以这样来写:
d+(?=)
上述的表达式就是说明,我现在断言整数 d+ 的 后面 能 匹配表达式:
让我们来验证下结果:
相应的正向后行断言可以这样写表达式:
(?<=阅读数:)d+
上述的表达式就是说明,我现在断言整数 d+ 的 前面 能 匹配表达式: 阅读数:
验证下结果如下:
分组
正则表达式中用小括号 () 来做分组,也就是括号中的内容作为一个整体。
因此当我们要匹配分组 he 的时候,可以用下面这个表达式 :
(he)
我们看到正则表达式用小括号来做分组,那么问题来了:
如果要匹配的字符串中本身就包含小括号,那应该怎么办?
针对这种情况,正则提供了转义的方式,也就是要把这些元字符、限定符或者关键字转义成普通的字符,做法很简单,就是在要转义的字符前面加个斜杠()即可。
因此当我们要匹配分组 (he) 的时候,可以用下面这个表达式 :
((he))
下面我们用一个正则表达式的图形生成工具,做一个对比的实验,让我们对分组和定位有个了解。
1:匹配 he 分组一次 ;
2:匹配 he 分组零或多次;
3:匹配以 he 开头的分组一次;
4:匹配以 he 开头的分组零或多次
捕获与反向引用
单纯说到捕获,他的意思是匹配表达式,但捕获通常和分组联系在一起,也就是“捕获组”。
捕获组:
匹配子表达式的内容,把匹配结果保存到内存中以数字编号或显示命名的组里(可以把它想象为java中的array和map),以深度优先进行编号,之后可以通过序号或名称来使用这些匹配结果。
捕获组的表达式为: (exp) ,这个语法跟上面讲到的分组的概念是一样的,只是捕获将匹配到的分组,保存在了内存中,留待后面使用。具体怎么时候他不管,他只需要把匹配到的分组保存在内存中就可以了。
有一种情况当在匹配的过程中,需要与已经捕获到的分组进行匹配,这时就需要使用到保存在内存中的捕获组了,这种使用方式就被称为: 反向引用 。
假设我有这样一段文字:
aa12bb23cc34
现在我想拿到成对的字符,该怎么做呢?这种情况下通过断言或者其他方式是办不到的,那我们能否在匹配的过程中将匹配到的一个字符先保存在内存中,然后匹配下一个字符时再与上一个字符相比较,如果相等,就说明匹配成了,拿到了成对的字符了。
那首先我们先要写一个匹配单个字符分组的表达式:
(w)
那当匹配时捕获到一个字符分组时,我们需要将该字符引用出来,与下一个字符想比较,我们期望匹配的下一个字符也与我当前保存的字符相等,那么表达式就变成了这样:
(w)
这里的 表示的是,当前正则表达式匹配到的 第1个 分组,那就意味着, 表示 第2个 分组。
做个测试,结果如下:
那如果我想再匹配复杂一点的结果,比如:XYY 这种的结果,又该怎么写呢?
其实有了上面的基础之后就很简单了,我们需要做的就是 对捕获到的第2个分组进行反向引用 就可以了!
具体的表达式为:
(w)(w)
测试结果如下:
表示成图形就是这样:
常见正则
为了更加形象的了解正则表达式,我们最后通过图形的方式来了解一些常见的正则表达式,使用图形的目的是希望能对冷冰冰的表达式有个更深刻的认识。
以下是一些常见正则的表达式与图片,可能有些过时了,如电话号码出现了新的号段,但是大体上应该没有问题。
整数
[0-9]+
逗号分隔的整数
[0-9]{1,3}(,[0-9]{3})*
浮点数
(+?(d+|.d+|d+.d+)|-?(d+|d+.d+))
0-255之间的数字
^([0-9]|[0-9]{2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])$
身份证
1d{14}(d{2}[0-9x])?$
邮箱
2{0,64}@([a-zA-Z0-9]{1,63}.)*[-a-zA-Z0-9]{1,63}$
固定电话
((?0[1-9]{2,3})?-?)?[1-9][0-9]{6,7}(-[0-9]{1,6})?
邮编
[1-9][0-9]{5}
ISBN
((ISBN(-13)?:?s)?97[89][-s]?[0-9][-s]?[0-9]{3}[-s]?[0-9]{5}[-s]?[0-9]|(ISBN(-10)?:?s)?[0-9][-s]?[0-9]{3}[-s]?[0-9]{5}[-s]?[0-9x])
手机号
(0|+86)?(13[0-9]|15[0-356]|18[025-9])d{8}
成对的html标签
如 test
<([^>]+)>[sS]*?</>
a标签
<as+hrefs*=s*["’]?(["‘s]+)["’]?>([<]+)
head标签
([^>]+) ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190118120326139.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0F0aGxlbmFB,size_16,color_FFFFFF,t_70)image标签
<imgs[>]*?src=[’"]?(["’]+)["’]?[^>]*>
正则思维导图
正则表达式及java文本复杂操作
正则表达式课程规划
正则表达式基本知识:
基本语法
高级语法
练习
editplus,notpad++,ultraedit,eclipse中使用正则
JAVA复杂文本操作
正则表达式(Regular Expresssion)简介
为什么需要正则表达式?
文本的复杂处理。
正则表达式的优势和用途?
一种强大而灵活的文本处理工具;
大部分编程语言 、数据库、文本编辑器、开发环境都支持正则表达式。
正则表达式定义:
正如他的名字一样是描述了一个规则,通过这个规则可以匹配一类字符串。
学习正则表达式很大程度上就是学习正则表达式的语法规则。
开发中如何使用?
开发中使用正则表达式的流程:
分析所要匹配的数据,写出测试用的典型数据
在工具软件中进行匹配测试
在程序中调用通过测试的正则表达式
工具软件RegexBuddy
正则表达式语法(1)
普通字符
字母、数字、汉字、下划线、以及没有特殊定义的标点符号,都是“普通字符”。表达式中的普通字符,在匹配一个字符串的时候,匹配与之相同的一个字符。
简单的转义字符\n\t\^$(
标准字符集合:
能够与 ‘多种字符’ 匹配的表达式
注意区分大小写,大写是相反的意思
\D非数字
\W除了字母数字 下划线以外的
\S非空格制表符换行符
自定义字符集合:[ ^ 1-3a-n]
[ ]方括号匹配方式,能够匹配方括号中任意一个字符
正则表达式的特殊符号,被包含到中括号中,则失去特殊意义,除了^,-之外。
标准字符集合,除小数点外,如果被包含于中括号,自定义字符集合将包含该集合。比如:
[\d.-+]将匹配:数字、小数点、+、-
量词(Quantifier){n}{m,n}{m,}?+*
修饰匹配次数的特殊符号
匹配次数中的贪婪模式(匹配字符越多越好,默认!)
匹配次数中的非贪婪模式(匹配字符越少越好,修饰匹配次数的特殊符号后再加上一个 “?” 号)
非贪婪
?代表0次或1次
+至少一次
*0次或多次
字符边界^ $ \b
(本组标记匹配的不是字符而是位置,符合某种条件的位置)
\b匹配这样一个位置:前面的字符和后面的字符不全是\w–字母数字下划线
零宽
^字符串开始的地方
$字符结束的地方
\b单词的边界
\b匹配这样一个位置:前面的字符和后面的字符不全是\w–字母数字下划线
正则表达式的匹配模式
IGNORECASE 忽略大小写模式
匹配时忽略大小写。
默认情况下,正则表达式是要区分大小写的。
SINGLELINE 单行模式
整个文本看作一个字符串,只有一个开头,一个结尾。
使小数点 “.” 可以匹配包含换行符(\n)在内的任意字符。
MULTILINE 多行模式
每行都是一个字符串,都有开头和结尾。
在指定了 MULTILINE 之后,如果需要仅匹配字符串开始和结束位置,可以使用 \A 和 \Z
多行模式
选择符和分组 | ()(?expression)
反向引用(\nnn)
每一对()会分配一个编号,使用 () 的捕获根据左括号的顺序从 1 开始自动编号。
通过反向引用,可以对分组已捕获的字符串进行引用。
反向引用,利用前面的捕获组
预搜索(零宽断言) (?=exp)
只进行子表达式的匹配,匹配内容不计入最终的匹配结果,是零宽度
这个位置应该符合某个条件。判断当前位置的前后字符,是否符合指定的条件,但不匹配前后的字符。是对位置的匹配。
正则表达式匹配过程中,如果子表达式匹配到的是字符内容,而非位置,并被保存到最终的匹配结果中,那么就认为这个子表达式是占有字符的;如果子表达式匹配的仅仅是位置,或者匹配的内容并不保存到最终的匹配结果中,那么就认为这个子表达式是零宽度的。占有字符还是零宽度,是针对匹配的内容是否保存到最终的匹配结果中而言的。
匹配以ing结尾的,
ing没有匹配到
匹配以数字结尾的
数字本身是没有匹配到的
后面不能是数字
位置前面应该怎么样
位置后面应该怎么样
课堂练习1!
电话号码验证
(1)电话号码由数字和"-“构成
(2)电话号码为7到8位
(3)如果电话号码中包含有区号,那么区号为三位或四位, 首位是0.
(4)区号用”-"和其他部分隔开
(5)移动电话号码为11位
(6)11位移动电话号码的第一位和第二位为"13“,”15”,”18”
课堂练习2!
电子邮件地址验证
1.用户名:字母、数字、中划线、下划线组成。
2.@
3.网址:字母、数字组成。
4. 小数点:.
5. 组织域名:2-4位字母组成。
不区分大小写
常用正则表达式列表
其他妙用
开发环境和文本编辑器中使用正则
eclipse
Notepad++
Editplus
UltraEdit
数据库中也可以使用正则
Mysql5.5以上
Oracle10g以上
例如:
SELECT prod_nameFROM productsWHERE prod_name REGEXP ‘.000’
JAVA程序中使用正则表达式
相关类位于:java.util.regex包下面
类 Pattern:
正则表达式的编译表示形式。
Pattern p = Pattern.compile(r,int); //建立正则表达式,并启用相应模式
类 Matcher:
通过解释 Pattern 对 character sequence 执行匹配操作的引擎
Matcher m = p.matcher(str); //匹配str字符串
matches尝试将整个字符序列与该模式匹配
find该方法扫描输入的序列,查找与该模式匹配的下一个子序列
找到后把匹配的内容打印出来
直接写个循环
替换和分隔
把数字找到后替换为符号
替换并不会改变原有字符串的结构,只是生成了一个新的字符串
测试分隔
按照数字进行切割,可以用正则切割比较复杂的边界
小练习
打开网易的网站,把所有的超链接都取出来
其实也是网络爬虫的一个应用
把这段代码封装成一个方法,你给我传入一个地址,我给你返回结果
获得url对应的网页源码的内容
接下来写一个正则表达进行匹配超链接
要取得超链接的地址
打开流的时候设置编码
写正则
把正则里面的内容取出来
把地址取出来
使用分组或者预搜索
封装