1.1 什么是正则表达式 正则表达式 : 定义一个搜索模式的字符串。
正则表达式可以用于搜索、编辑和操作文本。
正则对文本的分析或修改过程为:首先正则表达式应用的是文本字符串(text/string),它会以定义的模式从左到右匹配文本,每个源字符只匹配一次。
1.2 示例
正则表达式匹配this is text精确匹配字符串 “this is text”this\s+is\s+text匹配单词 “this” 后跟一个或多个空格字符,后跟词 “is” 后跟一个或多个空格字符,后跟词 “text” ^\d+(.\d+)?^ 定义模式必须匹配字符串的开始,d+ 匹配一个或多个数字,? 表明小括号内的语句是可选的,. 匹配 “.”,小括号表示分组。例如匹配:“5”、“1.5” 和 “2.21”2.1 常见匹配符号
正则表达式描述.匹配所有单个字符,除了换行符(Linux 中换行是 \n,Windows 中换行是 \r\n)^regex正则必须匹配字符串开头regex$正则必须匹配字符串结尾 [abc]复选集定义,匹配字母 a 或 b 或 c [abc][vz]复选集定义,匹配字母 a 或 b 或 c,后面跟着 v 或 z [^abc]当插入符 ^ 在中括号中以第一个字符开始显示,则表示否定模式。此模式匹配所有字符,除了 a 或 b 或 c[a-d1-7]范围匹配,匹配字母 a 到 d 和数字从 1 到 7 之间,但不匹配 d1XZ匹配 X 后直接跟着 ZXZ2.2 元字符
元字符是一个预定义的字符。
正则表达式描述\d匹配⼀个数字,是 [0-9] 的简写\D匹配⼀个⾮数字,是 [^0-9] 的简写\s匹配⼀个空格,是 [ \t\n\x0b\r\f] 的简写\S匹配⼀个⾮空格\w匹配⼀个单词字符(⼤⼩写字⺟、数字、下划线),是 [a-zAZ_0-9] 的简写\W匹配⼀个⾮单词字符(除了⼤⼩写字⺟、数字、下划线之外的字符),等同于 [^\w]2.3 限定符 限定符定义了一个元素可以发生的频率。
正则表达式描述举例*匹配 >=0 个,是 {0,} 的简写X* 表示匹配零个或多个字⺟ X,.* 表示匹配任何字符串+匹配 >=1 个,是 {1,} 的简写+ 匹配 >=1 个,是 {1,} 的简写?匹配 1 个或 0 个,是 {0,1} 的简写X? 表示匹配 0 个或 1 个字⺟ X{X}只匹配 X 个字符\d{3} 表示匹配 3 个数字,.{10} 表示匹配任何⻓度是 10 的字符串{X,Y}匹配 >=X 且 <=Y 个\d{1,4} 表示匹配⾄少 1 个最多 4 个数字*?如果 ? 是限定符 * 或 + 或 ? 或 {} 后⾯的第⼀个字符,那么表示⾮贪婪模式(尽可能少的匹配字符),⽽不是默认的贪婪模式2.4 分组和反向引用 小括号 () 可以达到对正则表达式进行分组的效果。
模式分组后会在正则表达式中创建反向引用。反向引用会保存匹配模式分组的字符串片断,这使得我们可以获取并使用这个字符串片断。
在以正则表达式替换字符串的语法中,是通过 $ 来引用分组的反向引用,$0 是匹配完整模式的字符串(注意在 JavaScript 中是用 $& 表示);$1 是第一个分组的反向引用;$2 是第二个分组的反向引用,以此类推。 示例:
package com.wuxianjiezh.demo.regex; package com.wuxianjiezh.demo.regex; public class RegexTest { public static void main(String[] args) { // 去除单词与 , 和 . 之间的空格 String Str = "Hello , World ."; String pattern = "(\\w)(\\s+)([.,])"; // $0 匹配 `(\w)(\s+)([.,])` 结果为 `o空格,` 和 `d空格.` // $1 匹配 `(\w)` 结果为 `o` 和 `d` // $2 匹配 `(\s+)` 结果为 `空格` 和 `空格` // $3 匹配 `([.,])` 结果为 `,` 和 `.` System.out.println(Str.replaceAll(pattern, "$1$3")); // Hello, World. } }上面的例子中,我们使用了 [.] 来匹配普通字符 . 而不需要使用 [\.]。因为正则对于 [] 中的 .,会自动处理为 [.],即普通字符 . 进行匹配。
2.4.1 仅分组但无反向引用
当我们在小括号 () 内的模式开头加入 ?:,那么表示这个模式仅分组,但不创建反向引用。 示例:
package com.wuxianjiezh.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexTest { public static void main(String[] args) { String str = "img.jpg"; // 分组且创建反向引⽤ Pattern pattern = Pattern.compile("(jpg|png)"); Matcher matcher = pattern.matcher(str); while (matcher.find()) { System.out.println(matcher.group()); System.out.println(matcher.group(1)); } } }运⾏结果:
jpg jpg若源码改为:
package com.wuxianjiezh.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexTest { public static void main(String[] args) { String str = "img.jpg"; // 分组但不创建反向引⽤ Pattern pattern = Pattern.compile("(?:jpg|png)"); Matcher matcher = pattern.matcher(str); while (matcher.find()) { System.out.println(matcher.group()); System.out.println(matcher.group(1)); } } }运⾏结果:
jpg Exception in thread "main" java.lang.IndexOutOfBoundsException: No group 1 at java.util.regex.Matcher.group(Matcher.java:538) at com.wuxianjiezh.regex.RegexTest.main(RegexTest.java:15)2.4.2 分组的反向引⽤副本 Java 中可以在⼩括号中使⽤ ? 将⼩括号中匹配的内容保存为⼀个名字为 name 的副本。 示例:
package com.wuxianjiezh.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexTest { public static void main(String[] args) { String str = "@wxj 你好啊"; Pattern pattern = Pattern.compile("@(?<first>\\w+\\s)"); // 保存⼀个副本 Matcher matcher = pattern.matcher(str); while (matcher.find()) { System.out.println(matcher.group()); System.out.println(matcher.group(1)); System.out.println(matcher.group("first")); } } }运⾏结果:
@wxj wxj wxj2.5 否定先⾏断⾔(Negative lookahead) 我们可以创建否定先⾏断⾔模式的匹配,即某个字符串后⾯不包含另⼀个字符串的匹配模式。 否定先⾏断⾔模式通过 (?!pattern) 定义。⽐如,我们匹配后⾯不是跟着 “b” 的 “a”:
a(?!b)
2.6 指定正则表达式的模式 可以在正则的开头指定模式修饰符。
(?i) 使正则忽略⼤⼩写。 (?s) 表示单⾏模式(“single line mode”)使正则的 . 匹配所有字符,包括换⾏符。 (?m) 表示多⾏模式(“multi-line mode”),使正则的 ^ 和 $ 匹配字符串中每⾏的开始和结束。
2.7 Java 中的反斜杠
反斜杠 \ 在 Java 中表示转义字符,这意味着 \ 在 Java 拥有预定义的含义。
这⾥例举两个特别重要的⽤法:
1、在匹配 . 或 { 或 [ 或 ( 或 ? 或 $ 或 ^ 或 * 这些特殊字符时,需要在前⾯加上 \,⽐如匹配 . 时,Java 中要写为 \.,但对于正则表达式来说就是 .。 2、在匹配 \ 时,Java 中要写为 \\,但对于正则表达式来说就是 \。
注意:Java 中的正则表达式字符串有两层含义,⾸先 Java 字符串转义出符合正则表达式语法的字符 串,然后再由转义后的正则表达式进⾏模式匹配。
2.8 易错点示例 [jpg|png] 代表匹配 j 或 p 或 g 或 p 或 n 或 g 中的任意⼀个字符。 (jpg|png) 代表匹配 jpg 或 png。
3.1 内置的字符串正则处理⽅法 在 Java 中有四个内置的运⾏正则表达式的⽅法,分别是 matches()、split())、replaceFirst()、replaceAll()。注意 replace() ⽅法不⽀持正则表达式。
⽅法描述s.matches(“regex”)当仅且当正则匹配整个字符串时返回 trues.split(“regex”)按匹配的正则表达式切⽚字符串 s.replaceFirst(“regex”, “replacement”)替换⾸次匹配的字符串⽚段s.replaceAll(“regex”, “replacement”)替换所有匹配的字符3.2 示例 示例代码:
package com.wuxianjiezh.regex; public class RegexTest { public static void main(String[] args) { System.out.println("wxj".matches("wxj")); System.out.println("----------"); String[] array = "w x j".split("\\s"); for (String item : array) { System.out.println(item); } System.out.println("----------"); System.out.println("w x j".replaceFirst("\\s", "-")); System.out.println("----------"); System.out.println("w x j".replaceAll("\\s", "-")); } }运⾏结果:
true ---------- w x j ---------- w-x j ---------- w-x-jJava 中使⽤正则表达式需要⽤到两个类,分别
为 java.util.regex.Pattern 和 java.util.regex.Matcher。
第⼀步,通过正则表达式创建模式对象 Pattern。
第⼆步,通过模式对象 Pattern,根据指定字符串创建匹配对象 Matcher。
第三步,通过匹配对象 Matcher,根据正则表达式操作字符串。
来个例⼦,加深理解:
package com.wuxianjiezh.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexTest { public static void main(String[] args) { String text = "Hello Regex!"; Pattern pattern = Pattern.compile("\\w+"); // Java 中忽略⼤⼩写,有两种写法: // Pattern pattern = Pattern.compile("\\w+", Pattern.CASE_INSENSITIVE); // Pattern pattern = Pattern.compile("(?i)\\w+"); // 推荐写法 Matcher matcher = pattern.matcher(text); // 遍例所有匹配的序列 while (matcher.find()) { System.out.print("Start index: " + matcher.start()); System.out.print(" End index: " + matcher.end() + " "); System.out.println(matcher.group()); } // 创建第两个模式,将空格替换为 tab Pattern replace = Pattern.compile("\\s+"); Matcher matcher2 = replace.matcher(text); System.out.println(matcher2.replaceAll("\t")); }运⾏结果:
Start index: 0 End index: 5 Hello Start index: 6 End index: 11 Regex Hello Regex!5.1 中⽂的匹配 [\u4e00-\u9fa5]+ 代表匹配中⽂字。
package com.wuxianjiezh.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexTest { public static void main(String[] args) { String str = "閑⼈到⼈间"; Pattern pattern = Pattern.compile("[\\u4e00-\\u9fa5]+"); Matcher matcher = pattern.matcher(str); while (matcher.find()) { System.out.println(matcher.group()); } } }运⾏结果:
閑⼈到⼈间5.2 数字范围的匹配
⽐如,匹配 1990 到 2017。
注意:这⾥有个新⼿易范的错误,就是正则 [1990-2017],实际这个正则只匹配 0 或 1 或 2 或 7 或 9 中的任⼀个字符。
正则表达式匹配数字范围时,⾸先要确定最⼤值与最⼩值,最后写中间值。
正确的匹配⽅式:
package com.wuxianjiezh.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexTest { public static void main(String[] args) { String str = "1990\n2010\n2017"; // 这⾥应⽤了 (?m) 的多⾏匹配模式,只为⽅便我们测试输出 // "^1990$|^199[1-9]$|^20[0-1][0-6]$|^2017$" 为判断 1990-2017 正确的正则表达式 Pattern pattern = Pattern.compile("(?m)^1990$|^199[1-9]$|^20[0-1][0-6]$|^2017$"); Matcher matcher = pattern.matcher(str); while (matcher.find()) { System.out.println(matcher.group()); } } }运⾏结果:
1990 2010 20175.3 img 标签的匹配 ⽐如,获取图⽚⽂件内容,这⾥我们考虑了⼀些不规范的 img 标签写法:
package com.wuxianjiezh.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexTest { public static void main(String[] args) { String str = "<img src='aaa.jpg' /><img src=bbb.png/><img src=\"ccc.png\"/>" +"<img src='ddd.exe'/><img src='eee.jpn'/>"; // 这⾥我们考虑了⼀些不规范的 img 标签写法,⽐如:空格、引号 Pattern pattern = Pattern.compile("<img\\s+src=(?:['\"])?(?<src>\\w+.(jpg|png))(?:['\"])?\\s*/>"); Matcher matcher = pattern.matcher(str); while (matcher.find()) { System.out.println(matcher.group("src")); } } }运⾏结果:
aaa.jpg bbb.png ccc.png5.4 贪婪与⾮贪婪模式的匹配 ⽐如,获取 div 标签中的⽂本内容:
package com.wuxianjiezh.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexTest { public static void main(String[] args) { String str = "<div>⽂章标题</div><div>发布时间</div>"; // 贪婪模式 Pattern pattern = Pattern.compile("<div>(?<title>.+)</div>"); Matcher matcher = pattern.matcher(str); while (matcher.find()) { System.out.println(matcher.group("title")); } System.out.println("--------------"); // ⾮贪婪模式 pattern = Pattern.compile("<div>(?<title>.+?)</div>"); matcher = pattern.matcher(str); while (matcher.find()) { System.out.println(matcher.group("title")); } } }运⾏结果:
⽂章标题</div> <div>发布时间 -------------------------------------------------- ⽂章标题 发布时间JavaScript、Python 等的在线表达式⼯具:https://regex101.com/ Java 在线表达式⼯具:http://www.regexplanet.com/advanced/java/index.html
参考链接: https://segmentfault.com/a/1190000009162306