正则表达式

一、正则表达式是什么?

正则表达式就是一个用于匹配字符串的字符串模板,可以匹配一批字符串。在实际开发中,会遇到很多处理字符串的情景,当然String类已经提供了很多方法方便我们处理字符串,但是有的时候需要处理的规则比较复杂,往往在这些时候,使用正则表达式可以让问题迎刃而解。可以说正则表达式就是处理字符串的神器。还有就是,精通正则可以装B,因为大部分程序猿对正则的使用还是比较少的,如果你精通了正则,就可以在朋友面前秀一把。

二、怎么创建正则表达式?要创建正则表达式需要懂得正则的语法,语法如下

1.正则表达式支持的合法字符如下表所示

使用最多的是第一行的x

字符 解释
x 代表一个字符,比如1、2、a、#等,在这里x代表的是任何合法字符
\0xxx 代表一个八进制数,x代表 0 ~ 7 ,如0271
\xhhh 代表一个十六进制数,h代表 0 ~ 9和A ~ F,如0xAC9D
\uhhhh 代表一个十六进制数字0xhhhh所表示的Unicode字符
\t 代表制表符(\u0009)
\n 代表一个换行符(\u000A)
\r 代表一个回车符(\u000C)
\f 代表一个换页符(\u000C)
2.特殊字符

正则表达式中有一些特殊字符,在正则语法里面有它自身的含义,如果要匹配特殊字符本身需要使用其对应转义字符,如下表所示。

特殊字符 解释
$ 匹配一行的结尾。如果要匹配$本身,使用 $
^ 匹配一行的开头。如果要匹配^本身,使用 \^
( ) 标记子表达式的开始和结束为止。要匹配他们,使用 \( 和 \)
[ ] 用于确定中括号表达式开始和结束的位置。要匹配他们,使用 \[ 和 \]
{ } 用于标记前面的子表达式出现的频度。要匹配他们,使用 \{ 和 \}
* 指定前面子表达式可以出现0次或多次。 要匹配*本身,使用 \*
+ 指定前面子表达式可以出现1次或多次。要匹配+本身,使用 \+
? 指定前面子表达式可以出现0次或1次。要匹配?本身,使用 \?
. 匹配出换行符\n之外的任何单词字符。要匹配 . 本身,使用 \.
/ 用于转义下一个字符,或指定八进制、十六进制字符。如果需要匹配\本身,使用 \\
| 正定在两项之间任选一项。如果要匹配 | 本身,使用 |

注意
在Java里,反斜杠 \ 本身具有特殊含义,如果要用正则匹配反斜杠本身,那么对应的正则表达式是4个反斜杠,即 ‘\\\\’。因为在正则语法里匹配反斜杠本身需要进行一次转义,即 ‘\\’ 。到了java里面,还需要还需要对 ‘\\’ 中的两个反斜杠再进行一次转义,因此变成了4个斜杠 ‘\\\\’。如果要匹配 ‘(’,在java里面就是 ‘\\(’。

3. 通配符

通配符是可以匹配一类字符的特殊字符,也被叫做预定义字符,如下表所示。

通配符 解释
. 匹配任何字符
\d 匹配 0 ~ 9 的所有数字
\D 匹配非数字
\s 匹配所有空白字符,包括空客、制表符、回车符、换页符、换行符等
\S 匹配所有非空白字符
\w 匹配所有单词字符,包括 0 ~ 9 所有数字、26个英文字母(不区分大小写)和下划线
\W 匹配所有非单词字符

举例
‘\d\d\d - \d\d\d\d\d\d\d\d’ 这个正则可以匹配 028 - 24619271这种电话号码。注意哦,在java里的话也需要对这里的反斜杠进行转义,转义后对应的正则是 ‘\\d\\d\\d - \\d\\d\\d\\d\\d\\d\\d\\d’ 。你是不是觉得这里写了这么多个 \d 太啰嗦了,没错,继续往后面看,后面会有解决的。

4.方括号表达式

在有些特殊的情况下,指向匹配 a ~ h 的字母、或者匹配出ab之外的所有小写字母、或者匹配所有的中文字符。怎么办,上面介绍的这些东西就无能为力了,好在我们还有方括号表达式,如下表所示。

方括号表达式 解释
表示枚举 例如 [abc], 表示a、b、c其中任意一个字符;
[12Gh],表示1、2、G、h其中的任一字符;
表示范围 例如 [a-f],表示a ~ f范围内的任意字符;
[\u0041-\u0056],表示十六进制字符\u0041到\u0056范围的字符;
范围可以和枚举结合使用,如[a-dh-z]表示ad、hz范围内的任一字符;
表示取非:^ 例如 [abc]表示非a、b、c的任意字符;[h-z],表示非h~z范围内的任意字符;
表示 '与' 运算:&& 例如 [a-z&&[def]],表示求az与d、e、f的交集,即d、e、f;<br/>[a-z&&[^bc]],表示az范围内除了b、c的字符,与[ad-z]等价;
[a-z&&[^m-p]],表示az范围内除了mp范围内的任一字符,与[a-lq-z]等价;
表示 '并' 运算 并运算和前面的枚举类似,如 [a-d[m-p]],表示[a-dm-p]
5.圆括号表达式

正则表达式还支持圆括号表达式,用于将多个表达式组成一个表达式,圆括号表达式还可以使用或运算符 ‘|’,语法很简单。

例如: ((private)|(protected)|(public))用于匹配Java的访问控制符之一

6.边界匹配符

边界匹配符可以匹配一些元素的边界,其中 ^ 和 $ 用的最多,如下表所示

边界匹配符 解释
^ 行的开头
$ 行的结尾
\b 单词的边界
\B 非单词的边界
\A 输入的开头
\G 前一个匹配的结尾
\Z 输入的结尾,进用于最后的结束符
\z 输入的结尾
7.量词

前面写过一个正则表达式是 ‘\d\d\d - \d\d\d\d\d\d\d\d’,这看起十分繁琐,好在正则语法提供了量词来解决这个问题,量词说明如下表所示

量词 说明
X? X表达式出现0次或1次
X* X表达式出现0次或多次
X+ X表达式至少出现1次
X{n} X表达式出现n次
X{n,} X表达式至少出现n次
X{n,m} X表达式至少出现n次,至多出现m次

有了量词后,我们可以把 ‘\d\d\d - \d\d\d\d\d\d\d\d’ 改成 ‘\d{3}-\d{8}’

注意:量词匹配模式
在java中,量词的匹配模式有三种:

  • 贪婪模式:默认的就是贪婪模式,贪婪模式会一直匹配下去直到无法匹配。比如 ‘\w{2, 10}’ ,表示最少匹配2次,但是默认是贪婪匹配,匹配到2次后不会停止,会一直匹配下去,直到匹配达到10次或者不能再匹配到字符,只要匹配到2-10次都算成功。
  • 勉强模式:用 ?作为量词的后缀,就像 ‘\w{2,10}?’ 这样,勉强模式会按照最少次数去匹配。比如 ‘\w{2,10}’ 表示对\w匹配2次或10次,但是如果在后面加上?变成‘\w{2,10}?’,变成了勉强模式,就会按照最少匹配,即匹配到2次就算成功。
  • 占有模式:用 + 作为量词的后缀,就像 ‘\w*{2,10}+’ 这样,和贪婪模式一样,占有模式会尽可能多的去匹配,但是贪婪模式匹配不到字符后会有回溯操作,而占有模式没有回溯操作,这个回溯可以再写一篇文章了,为了篇幅原因,这里不过多介绍,不过下面会提供参考文章链接。

关于贪婪模式、勉强模式、占有模式这里做了简单介绍,平时使用的最多的还是贪婪模式,但是贪婪模式会有回溯陷阱,具体可以参考以下文章,作者写的很好,他介绍了正则原理和回溯机制,可以帮助我们更好的理解这三种模式,写出性能更好的正则表达式。
正则表达式三种模式:贪婪模式、懒惰模式、独占模式

三、上面介绍了正则表达式的语法,下面就说在java里面怎么使用正则表达式吧

java中与正则表达式相关的类主要是Pattern和Matcher,Pattern类是不可变类,可以让多个线程并发安全使用。Pattern对象是正则表达式编译之后在内存中的存在形式,因此正则表达式字符串必须先被编译为Pattern对象,然后再利用Pattern对象创建对应的Matcher对象。执行匹配所涉及的状态保留在Matcher对象中,多个Matcher对象可共享一个Pattern对象。

典型的使用流程如下:

 // 将一个正则表达式编译成Pattern对象
Pattern pattern = Pattern.compile("ab{1,3}V");
// 使用Pattern对象创建Matcher对象
Matcher matcher = pattern.matcher("abbV");
// 尝试将整个字符串与正则表达式进行匹配,
boolean matches = matcher.matches();

上面代码中定义的Pattern对象可以重复多次使用,如果某个正则表达式仅需使用一次,则可以使用Pattern类的静态方法matches(),如下:

boolean matches = Pattern.matches("ab{1,3}V", "abbV");

需要注意的是,如果正则表达式需要重复使用,建议提前将它编译好,作为一个成员对象保存在内存中,否则每次重新编译正则表达式,效率不高。

Matcher类提供常用方法如下:

  • find():返回目标字符串是否包含了Pattern匹配的子串
  • group():返回上一次与Pattern匹配的子串
  • start():返回上一次与Pattern匹配的自传在目标字符串中的开始位置
  • end():返回上一次与Pattern匹配的自传在目标字符串中的结束为止加1
  • matches():返回整个目标字符串与Pattern事发后匹配
  • lookingAt():和matches()方法类似,matches方法是将整个目标字符串进行匹配,而该方法是将目标字符串前缀部分与Pattern匹配
  • reset():将现有的Matcher对象应用于一个新的字符序列

下面写个小demo来展示这些方法的使用

@Test
public void regexDemo() {
    // webCotent是从网上截取的一小段内容,我们用正则匹配出里面所有的电话号码,实现一个爬虫小demo
    String webContent = "\n" + "深圳移动\n" + "移动选号\n" + "情侣号码\n" + "家庭号码\n" + "移动资费\n" + "移动营业厅\n" + "深圳联通\n" + "联通选号\n" + "情侣号码\n" + "家庭号码\n" +
                "联通资费\n" + "联通营业厅\n" + "深圳电信\n" + "电信选号\n" + "情侣号码\n" + "家庭号码\n" + "电信资费\n" + "电信营业厅\n" + "特色服务\n" + "回收号码\n" + "订制靓号\n" +
                "情侣号匹配\n" + "风水靓号\n" + "寄卖号码\n" + "号码测吉凶\n" + "\n" + "    1 2 3 4 \n" + "\n" + "    优惠快报\n" + "\n" + "    工信部:电话用户真实身份信息登...\n" +
                "    北京号码订制特价-联通185靓号最...\n" + "    冰激凌手机号码来啦,快抢购吧\n" + "    手机靓号的价值意义\n" + "    手机号码测吉凶到底靠谱吗?\n" + "    投资手机号码的好处\n" +
                "    手机靓号的发展历程\n" + "    哪些人最喜欢手机靓号?\n" + "    139手机号码靓号有什么特别的意...\n" + "    经典手机靓号的小故事\n" + "\n" + "会员登录\n" + "会员注册\n" +
                "\n" + "特价靓号限时抢购\n" + "距离结束0:50:56\n" + "\n" + "    13699877999\n" + "    深圳移动\n" + "    卡费:¥34203  话费:¥0\n" + "    13823138888\n" + "    深圳移动\n" +
                "    卡费:议价  话费:¥100\n" + "    18107556789\n" + "    深圳电信\n" + "    卡费:¥35000  话费:¥100\n" + "    13823335000\n" + "    深圳移动\n" + "    卡费:¥11319  话费:¥0\n" +
                "    13802591000\n" + "    深圳移动\n" + "    卡费:¥13985  话费:¥55\n" + "\n" + "    18566666630\n" + "    深圳联通\n" + "    卡费:¥24385  话费:¥55\n" +
                "    13823621666\n" + "    深圳移动\n" + "    卡费:¥37385  话费:¥55\n" + "    13631650000\n" + "    深圳移动\n" + "    卡费:¥41545  话费:¥55\n" + "    13828888815\n" +
                "    深圳移动\n" + "    卡费:¥49345  话费:¥55\n" + "    13823260888\n" + "    深圳移动\n" + "    卡费:¥62345  话费:¥55\n" + "\n" + "    13823291888\n" +
                "    深圳移动\n" + "    卡费:¥62345  话费:¥55\n" + "    13809881899\n" + "    深圳移动\n" + "    卡费:¥11200  话费:¥0\n" +
                "    13808808006\n" + "    深圳移动\n" + "    卡费:¥11200  话费:¥0\n" + "    13556699336\n" + "    深圳移动\n" + "    卡费:¥13499  话费:¥10\n" +
                "    15602318888\n" + "    深圳联通\n" + "    卡费:¥60840  话费:¥0\n" + "\n" + "    13265629999\n" + "    深圳联通\n" + "    卡费:¥49140  话费:¥0\n" +
                "    13538088885\n" + "    深圳移动\n" + "    卡费:¥20800  话费:¥0\n" + "    15012991299\n" + "    深圳移动\n" + "    卡费:¥23400  话费:¥0\n" + "    18823331391\n" +
                "    深圳移动\n" + "    卡费:¥11880  话费:¥0\n" + "    13686888885\n" + "    深圳移动\n" + "    卡费:¥71500  话费:¥0\n" + "\n" + "    13823333328\n" +
                "    深圳移动\n" + "    卡费:¥52000  话费:¥0\n" + "    13662222220\n" + "    深圳移动\n" + "    卡费:¥71500  话费:¥0\n" + "    13686868681\n" + "    深圳移动\n" +
                "    卡费:¥65000  话费:¥0\n" + "    13603060306\n" + "    深圳移动\n" + "    卡费:¥58500  话费:¥0\n" + "    13902989889\n" + "    深圳移动\n" + "    卡费:¥58500  话费:¥0\n" +
                "\n" + "    13922862286\n" + "    深圳移动\n" + "    卡费:¥58500  话费:¥0\n" + "    18822822282\n" + "    深圳移动\n" + "    卡费:¥52000  话费:¥0\n" +
                "    13828888339\n" + "    深圳移动\n" + "    卡费:¥49400  话费:¥0\n" + "    13889918991\n" + "    深圳移动\n" + "    卡费:¥45500  话费:¥0\n" + "    18816881868\n" +
                "    深圳移动\n" + "    卡费:¥36400  话费:¥0\n" + "\n";
    Pattern pattern = Pattern.compile("\\d{11}");
    Matcher matcher = pattern.matcher(webContent);
    while (matcher.find()) {
        System.out.println("起始位置:" + matcher.start() + ",结束位置:" + matcher.end() + ",号码:" + matcher.group());
    }
}

结果如下:

起始位置:376,结束位置:387,号码:13699877999
起始位置:422,结束位置:433,号码:13823138888
起始位置:466,结束位置:477,号码:18107556789
起始位置:514,结束位置:525,号码:13823335000
起始位置:560,结束位置:571,号码:13802591000
起始位置:608,结束位置:619,号码:18566666630
起始位置:655,结束位置:666,号码:13823621666
起始位置:702,结束位置:713,号码:13631650000
起始位置:749,结束位置:760,号码:13828888815
起始位置:796,结束位置:807,号码:13823260888
起始位置:844,结束位置:855,号码:13823291888
起始位置:891,结束位置:902,号码:13809881899
起始位置:937,结束位置:948,号码:13808808006
起始位置:983,结束位置:994,号码:13556699336
起始位置:1030,结束位置:1041,号码:15602318888
起始位置:1077,结束位置:1088,号码:13265629999
起始位置:1123,结束位置:1134,号码:13538088885
起始位置:1169,结束位置:1180,号码:15012991299
起始位置:1215,结束位置:1226,号码:18823331391
起始位置:1261,结束位置:1272,号码:13686888885
起始位置:1308,结束位置:1319,号码:13823333328
起始位置:1354,结束位置:1365,号码:13662222220
起始位置:1400,结束位置:1411,号码:13686868681
起始位置:1446,结束位置:1457,号码:13603060306
起始位置:1492,结束位置:1503,号码:13902989889
起始位置:1539,结束位置:1550,号码:13922862286
起始位置:1585,结束位置:1596,号码:18822822282
起始位置:1631,结束位置:1642,号码:13828888339
起始位置:1677,结束位置:1688,号码:13889918991
起始位置:1723,结束位置:1734,号码:18816881868
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 正则表达式(regular expression)是一种可以在许多现代应用程序和编程语言中使用的特殊形式的代码模式...
    Android轮子哥阅读 11,384评论 4 16
  • 正则表达式学习笔记 一篇记录了学习正则表达式的笔记。 1. 转义字符 在 HTMl 中转义字符以 & 符号开头,分...
    heyi_let阅读 388评论 0 0
  • 正则表达式有很多流派,也有很多的特性,不同的语言支持度也是不一样的。本篇文章是写Python中的正则表达式的用法的...
    Moscow1147阅读 1,080评论 0 0
  • 简介 正则表达式是由普通字符(例如字符 a 到 z)以及特殊字符(称为"元字符")组成的文字模式。模式描述在搜索文...
    我为峰2014阅读 486评论 0 2
  • 正则表达式描述的是一种规则,符合这种限定规则的字符串我们认为它某种满足条件的,是我们所需的。在正则表达式中,主要有...
    Single_YAM阅读 735评论 0 4