正则是一套标准,各个语言都一样,只是各个语言的实现不太一样,下面就主要比较Java和JavaScript的正则
-
创建正则–简单使用
<script> //正则字面量 //包裹 console.log(/20\d\d/.test("2019"))//true console.log(/20\d\d/.test("2100"))//false let a = "r" console.log(/a/.test("345a"))//true console.log(eval(`/${a}/`).test("345a"))//false let content = "baidu.com" let reg = new RegExp("u")//使用对象的形式就不用//包裹了 console.log(reg.test(content))//true </script>
public static void main(String[] args) { //因为正则表达式可以用字符串来描述规则,所以都可以使用变量 String variable = "\\d"; String regex = "20\\d"+variable;//这相当于正则字面量的形式 System.out.println("2019".matches(regex));//true System.out.println("2100".matches(regex));//false boolean matches = Pattern.matches(regex, "2019");//这就是String类matches的源码 System.out.println(matches); Pattern pattern = Pattern.compile("20\\d\\d");//这相当于正则对象 Matcher matcher = pattern.matcher("2019"); while (matcher.find()){ System.out.println(matcher.group());//2019 } }
-
选择符
|
<script> let tel = "010-9999999"//true tel = "020-9999999"//true tel = "010"//true tel = "020"//false //,这样表示是否包含010或者020\-\d{7,8}整体匹配到的 //其实真实的意思是/[010|020]\-\d{7,8}/ console.log(/010|020\-\d{7,8}/.test(tel))//test是检测字符串是否包含正则匹配到的内容,只要包含就返回true </script>
public static void main(String[] args) { String content = "010-9999999";//false content = "020-9999999";//true content = "020-9999";//false content = "010";//true content = "020";//false System.out.println(content.matches("010|020-\\d{7,8}")); //这样写的意思是010或者020-\d{7,8} //即选择符右边的是一组,并且matches是整体匹配 //真实的意思是"[010-020]-\\d{7,8}" }
-
转义-关于转义的理解,js和Java是一样的
<script> //js中\是转义字符:作用是转义后面一个字符(只转义后面第一个字符),正则中\也是转义字符,由于字面量正则/是特殊字符,所以字面量要匹配/也需要转义\/ console.log("d"==="d")//true console.log("\d" === "d")//true 因为d没有特殊含义d转义后还是d // console.log("\")//这样写会报错的-- "\"表示一个"和一个普通的" console.log("\\")// \ console.log("\d+\.\d+")//d+.d+ console.log("\\d+\\.\\d+")//\d+\.\d+ console.log("\\\\")// \\ console.log("\\r\\")// \r\ </script
/** * 转义理解 * Java中\是转义字符,作用是转义后面一个字符(只转义后面第一个字符),把它当作普通字符 * ”“包裹起来表示字符串 "\"表示一个"和一个普通的",所以这样写是会报错的 * \\\\ 会输出\\ 可以这样理解 第一个\把第二个\转义了,两个最终输出一个普通的\ 第3个\把第四个\转义了,最终输出一个普通的\ 整体最后输出两个普通的\\ * \\r\\ 第一个\把第二个\转义 第3个\把第4个\转义 最终输出字符串 \r\ * * 正则当中\也是转义字符 作用后边的一个字符,整体变得特殊 例如 d在正则里就是普通的d \d在正则里就代表数字 * 所以正则匹配时要写\\d 因为\\d最终代表\d,在正则里才代表数字 */ public class RegTransfer { public static void main(String[] args) { System.out.println("\\");// \ System.out.println("\\\\");// \\ System.out.println("\\r\\");// \r\ System.out.println("234".matches("\\d")); } }
-
修饰符
-
i和g大小写
<script> let content = "123sd#&^%SD" let regStr = /[a-z]/ //默认是区分大小写的和非贪婪匹配的 得到的结果 s regStr = /[a-z]+/g//得到的结果sd regStr = /[a-z]+/ig//得到的结果sd SD let match = content.match(regStr); console.log(...match) </script>
public static void main(String[] args) { //大小写 String content = "123sd#&^%SD"; String regStr = "(?i)[a-z]+";//(?i)代表后边的忽略大小写不会计入分组 这种方式可以灵活的控制大小写 regStr = "[a-z]+"; Pattern pattern = Pattern.compile(regStr); pattern = Pattern.compile(regStr, Pattern.CASE_INSENSITIVE);//这样代表整体忽略大小写 Matcher matcher = pattern.matcher(content); while (matcher.find()) {//如果只想取匹配到的第一个这里用if就行了 System.out.println("找到了:"+matcher.group()); } }
-
多行操作符
<script> //多行匹配 let str = ` #1 js,90 # #2 java,86 # #3 djfi,3 # dsf #4 Kotlin,87 # ` let reg = /^\s*#\d+.+\s+#$/gm //m的意思是每行单独对待 let lessons = str.match(reg).map(v => { v = v.replace(/\s*#\d+\s*/,"").replace(/\s+#/,""); [name,price] = v.split(",") return {name,price} }) console.log(JSON.stringify(lessons))//[{"name":"js","price":"90"},{"name":"java","price":"86"},{"name":"Kotlin","price":"87"}] // </script>
fun main() { val str = """ #1 js,90 # #2 java,86 # #3 djfi,93 # dsf #4 Kotlin,87 # """ val regStr = "^\\s*#\\d+.+\\s+#$"; val pattern = Pattern.compile(regStr, Pattern.MULTILINE) val matcher = pattern.matcher(str) val list = mutableListOf<String>() while (matcher.find()) { list.add(matcher.group()) } val result = list.map { v -> val (name,price) = v.replace("\\s*#\\d+\\s*".toRegex(), "").replace("\\s+#".toRegex(),"").split(",") mapOf("name" to name,"price" to price) } println(result)//[{name=js, price=90}, {name=java, price=86}, {name=Kotlin, price=87}] }
public static void main(String[] args) { //多行匹配 String content = " #1 js,90 #\n" + " #2 java,86 #\n" + " #3 djfi,93 # dsf \n" + " #4 Kotlin,87 #\n" + " "; String regStr = "^\\s*#\\d+.+\\s+#$"; Pattern pattern = Pattern.compile(regStr, Pattern.MULTILINE);//如果不用MULTILINE模式是不行的 Matcher matcher = pattern.matcher(content); List<String> list = new ArrayList<>(); while (matcher.find()) { list.add(matcher.group()); } List<Map<String,String>> result = list.stream().map(s -> { String[] array = s.replaceAll("\\s*#\\d+", "").replaceAll("\\s#", "").split(","); HashMap<String,String> map =new HashMap<>(); map.put("name", array[0]); map.put("price", array[1]); return map; }).collect(Collectors.toList()); for (Map<String, String> map : result) { System.out.println(map.entrySet()); } }
-
-
分组()括住的代表一组以及原子组引用和原子组非捕获
public static void main(String[] args) { //()括起来的是第一组,从左边第一个(括号算是第一组,第二个(是第二组,后面用\1和\2用来引用分组的内容 // String content = "df的话覅的1991返回发1234覆盖3333erd12321-333999111"; // String regStr = "(\\d)(\\d)\\2\\1";//连续4位数,第1位和第四位相同,第二位和第三位相同 // String regStr = "(\\d)\\1{3}";//连续4位数,都相同 String regStr = "\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}";//前面是一个5位数,然后是一个-号,然后是一个9位数,连续的每3位相同 String content = "我....我要....学学学学....编程Java!";//结巴语句变成我哟啊学编程Java content = content.replaceAll("\\.", "").replaceAll("(.)\\1+","$1"); // String $1 = Pattern.compile("(.)\\1+").matcher(content).replaceAll("$1");//$1是在外部使用反向引用 // System.out.println($1); System.out.println(content);//我要学编程java Pattern pattern = Pattern.compile(regStr); Matcher matcher = pattern.matcher(content); while (matcher.find()) { System.out.println("找到了:"+matcher.group(0)); } //----------------------------------- 非捕获分组和js差不多 String str = "parade岁月山大佛i和房符合parade0393df发parade同学"; String regEStr = "parade(?:岁月|0393)";//不会捕获分组 // String regEStr = "parade(?:岁月|0393|同学)";//parade岁月和parade0393和parade同学 不会捕获分组 // String regEStr = "parade(岁月|0393)";//parade岁月和parade0393 会捕获分组 // String regEStr = "parade(?=岁月|0393)";//parade和parade 非捕获parade岁月的parade和parade0393的parade // String regEStr = "parade(?!岁月|0393)";//parade和parade 非捕获parade岁月的parade和parade0393的parade Pattern compile = Pattern.compile(regEStr);//parade后边不是岁月和0393的parade 和上面的是取反 Matcher match = compile.matcher(str); while (match.find()) { System.out.println(match.group(0)); // System.out.println(match.group(1));//使用非捕获分组,这里写就会报错IndexOutOfBoundsException } //其他的非捕获分组?<= ?<! 这些非捕获分组只是条件 准确的来说叫断言 修饰符是不能修饰这些断言的 }
<script> let content = "df的话覅的1991返回发1234覆盖3333erd12321-333999111" let reg = /(\d)(\d)\2\1/g//连续四位数,第一位和第4位相同,第2位和第3位相同 --1991和3333 reg = /(\d)\1{3}/g//连续4位数都相同--3333 reg = /\d{5}-(\d)\1{2}(\d)\2{2}(\d)\3{2}/g//前面是一个5位数,然后是一个-号,然后是一个9位数,连续的每3位相同--12321-333999111 content = "我....我要....学学学学....编程Java!"//我要学编程Java reg = /\./g content = content.replace(reg,"").replace(/(.)\1+/g,"$1") console.log(content)//我要学编程Java // console.log(content.match(reg))// content = "parade岁月山大佛i和房符合parade0393df发parade同学"; //reg = /parade(?<alias>岁月|0393)/g//会捕获分组 res[1]输出岁月和0393 ?<name>代表给分组起别名 // reg = /parade(?:岁月|0393)/g//不会捕获分组 res[1]输出undefined 这里需要使用g修饰符,否则只会匹配一个 // reg = /parade(?=0393|岁月)/g//不会捕获分组 reg = /parade(?!0393|岁月)/g//不会捕获分组 parade同学的parade let result = [] while ((res = reg.exec(content))){ result.push(res) // console.log(res[1])//代表第一个分组,使用?: // console.log(res.groups.alias)//和res[1]效果是一样的 alias是给分组起的别名 } console.log(result) </script>
-
贪婪匹配
/** * java默认是贪婪匹配的 * 任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的后边紧跟是非贪婪的 * 非贪婪匹配是尽可能少的匹配 */ public static void main(String[] args) { String content = "123000"; String regStr = "(\\d+?)(0*)";//如果"(\\d+)(0*)",那么第一组是123000,第二组是""空字符串 content = "hello234okdfsdf34ok"; regStr = "\\d+?ok";//此时"\\d+?ok"效果一样 regStr = "\\d+";//此时匹配 234和34 regStr = "\\d+?";//此时匹配 2 3 4 3 4 Pattern pattern = Pattern.compile(regStr); Matcher matcher = pattern.matcher(content); // if (matcher.matches()) {//matches是整体匹配 // System.out.println("整体找到:"+matcher.group()); // System.out.println("group1:"+matcher.group(1)); // System.out.println("group2:"+matcher.group(2)); // } StringBuffer buffer = new StringBuffer(); while (matcher.find()) { matcher.appendReplacement(buffer, "-"); System.out.println("找到了:" + matcher.group()); } matcher.appendTail(buffer);//如果没有这一句下面会输出hello---okdfsdf--,有这一句会输出hello---okdfsdf--ok System.out.println(buffer); }
<script> let content = "123000" let reg = /^(\d+)(0*)$///默认也是贪婪的 console.log(content.match(reg))//第一组是123000 reg = /^(\d+?)(0*)$///禁止贪婪 console.log(content.match(reg))//第一组是123 match默认不是整体匹配 </script>
-
方法使用对比
/** * String类的方法 * 1.split(String regex) 注意如果开头就匹配,那最终数组第一个元素是空字符 * 2.replaceAll(String regx) * 3.replaceFirst(String regx) * 4.matches(String regx)//注意此方法是整体匹配 部分匹配请使用Matcher 类的find方法 * Matcher类的方法 * 1.find * 2.matches 和String类的matches效果一样 * 3.lookAt() 从头开始找 非整体,找不到就返回false即有子字符串符合 * 4.replaceAll 和replaceFirst和String类的效果一样 * 5.appendReplacement 和 appendTail 结合 find可以在循环查找的时候定义每个匹配到的字符串的替换规则 */ public class RegSummary { public static void main(String[] args) { String content = "1parade岁月山大佛i和房符合parade0393df发parade同学"; String regStr = "parade(0393|岁月)"; String[] split = content.split(regStr); //注意如果开头就匹配,那最终数组第一个元素是空字符 // System.out.println(split.length);//3 for (String s : split) { // System.out.println(s);//山大佛i和房符合 和 df发parade同学 其实数组有3个元素,第一个是空字符 } //如果limit参数为 0 或 负数,则split()返回包含所有子字符串的数组。 //如果limit参数为正(例如n),则split()返回数组的最大长度。 String