1.regex(正则表达式):RegularExpressions(代替了StringTokenizer);字符串处理利器;在unix流行,perl使用regex更牛。
主要用在字符串匹配、查找和替换。例如:匹配IP(范围小于256)使用正则很好搞;从网页中揪出大量email地址发送垃圾邮件;从网页里揪出链接。包含Matcher(用模式匹配字符串后产生的结果)和pattern。
1 /* 2 * 告知此字符串是否匹配给定的正则表达式(也是一个字符串)。 3 */ 4 System.out.println("abc".matches("..."));//每个"."表示一个字符
1 /* 2 * 把字符串里的所有数字替换成"-",普通方法需要charAt逐个判断; 3 * "\\d"表示任意一个数字或者换成"[0-9]"; 4 * "\\D"表示任意一个非数字或者换成"[^0-9]" 5 */ 6 System.out.println("ab54564654sbg48746bshj".replaceAll("[0-9]", "-"));//每个"."表示一个字符
2.
1 /* 2 * compile将给定的正则表达式编译到模式中(每次编译需要费时间);{3}表示恰好三次。 3 * X{n} X,恰好 n 次 4 * X{n,} X,至少 n 次 5 * X{n,m} X,至少 n 次,但是不超过 m 次 6 */ 7 Pattern p = Pattern.compile("[a-z]{3}"); 8 Matcher m = p.matcher("ggs");//创建匹配给定输入与此模式的匹配器。内部实际上是创建了一个优先状态的自动机(编译原理) 9 //matcher和matches里待匹配的字符串实际上是CharSequence(接口),不过String实现了该接口,存在多态 10 System.out.println(m.matches());//若是"ggss"就不匹配了 11 //可一直接"ggs".matches("[a-z]{3}"),不过上面的有好处,至少效率高了,而且Pattern和Matcher提供了很多功能
3.在regex“. * +”中叫Meta Character;ctrl + shift + "/"表示注释,换成"\"表示去掉注释。
1 "a".matches(".");//true,"."表示任意一个字符,汉字也行 2 "aa".matches("aa");//true,也就是说普通字符串也可以作为正则表达式 3 /* 4 * true,"*"表示0或者多个字符,不过后面的要和第一个相同, 5 * 否则false,也就是判断字符串是否是单一字符组成的字符串 6 */ 7 "aaaa".matches("a*"); 8 "".matches("a*");//true 9 "aaa".matches("a?");//true,一次或者0次 10 "".matches("a?");//true 11 "a".matches("a?");//true 12 "544848154564113".matches("\\d{3,100}");//true 13 //这个是最简单的IP判断,不过若是超过255则判断不出来 14 "192.168.0.aaa".matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\d{1,3}"); 15 "192".matches("[0-2][0-9][0-9]");
4.[abc]表示匹配任意一个字符;[^abc]表示出了abc以外的其他字母(必须还是字母,若是空串也返回false)字符;[a-zA-Z]等价于"[a-z]|[A-Z]"是否是某个大小写字母;[A-Z&&[ABS]]表示大写字母中取ABS中任一个。
1 //发现|和||没区别,&和&&有区别,不知道这么理解对不对 2 System.out.println("C".matches("[A-Z&&[ABS]]"));//false 3 System.out.println("C".matches("[A-Z&[ABS]]"));//true 4 System.out.println("A".matches("[A-Z&&[ABS]]"));//true 5 System.out.println("A".matches("[A-Z&[ABS]]"));//true 6 System.out.println("C".matches("[A-Z|[ABS]]"));//true 7 System.out.println("C".matches("[A-Z||[ABS]]"));//true
5.\w 单词字符:[a-zA-Z_0-9] 进行用户名匹配时;\s 空白字符:[ \t\n\x0B\f\r]; \S 非空白字符:[^\s] ;\W 非单词字符:[^\w] 。
1 " \n\t\r".matches("\\s{4}");//true 2 " ".matches("\\S");//false 3 "a_8".matches("\\w{3}");//true 4 //“+”表示一次或者多次 5 "abc888&^%".matches("[a-z]{1,3}\\d+[&^#%]+");//true 6 /* 7 * 待匹配字符也只是一个反斜线,不过不可写成"\"那么和后面的"组合了, 8 * 前面的"无法匹配就会CE。 9 * 后面不可写成"\\",那么会运行错误(编译没问题),必须写成"\\\\" 10 */ 11 System.out.println("\\".matches("\\\\"));//true
6.POSIX 字符类(仅 US-ASCII)
\p{Lower} 小写字母字符:[a-z] ;\p{Upper} 大写字母字符:[A-Z] ;\p{ASCII} 所有 ASCII:[\x00-\x7F] ;\p{Alpha} 字母字符:[\p{Lower}\p{Upper}] ;\p{Digit} 十进制数字:[0-9] 。
7.边界匹配器
^ 行的开头
$ 行的结尾
\b 单词边界
\B 非单词边界
\A 输入的开头
\G 上一个匹配的结尾
\Z 输入的结尾,仅用于最后的结束符(如果有的话)
\z 输入的结尾
1 "hello world".matches("^h.*");//^行的开头 2 "hello world".matches(".*ld$");//$行的结尾 3 "hello world".matches("^h[a-z]{1,3}o\\b.*");//\b单词边界 4 "helloworld".matches("^h[a-z]{1,3}o\\b.*");
1 " \n".matches("^[\\s&&[^\\n]]*\\n$");//判断空白行,空白行开头是空白符
8.
还可以在find方法下使用m.start()和m.end()返回开始位置和结束位置的下一个;若是找不到则出错。
1 Pattern p = Pattern.compile("\\d{3,5}"); 2 String s = "133-34444-333-00"; 3 Matcher m = p.matcher(s); 4 m.matches();//matches匹配全部字符串 5 m.reset(); 6 /* 7 * 下面若是先调用了reset方法则输出true,true,true,false. 8 * 否则倒数第二个find也输出false。 9 * 原因如下: 10 * matches匹配到第一个"-"发现不匹配了,但是这四个字符已经被吃掉啦,再次匹配就从 11 * 34444开始了,第二个find从333,因为find匹配的是下一个子序列。 12 * reset方法让matches吃掉的字符串再吐出来。 13 * 综上:matches和find之间要使用reset,因为二者相互影响 14 * 15 */ 16 m.find(); 17 m.find(); 18 m.find();//尝试查找与该模式匹配的输入序列的下一个子序列 19 m.find(); 20 /* 21 * 尝试将从区域开头开始的输入序列与该模式匹配。 22 * Thinking in java的作者狠狠滴批评了这个方法,因为从字面看不出来到底从哪开始匹配。 23 * 下面全部是true,因为每次都从头开始 24 */ 25 m.lookingAt(); 26 m.lookingAt(); 27 m.lookingAt(); 28 m.lookingAt();
9.字符串替换
1 import java.util.regex.Matcher; 2 import java.util.regex.Pattern; 3 4 public class TestRegexReplacement { 5 6 public static void main(String[] args) { 7 8 Pattern p = Pattern.compile("java",Pattern.CASE_INSENSITIVE);//后面的参数是整形,表示“大小写不敏感” 9 Matcher m = p.matcher("Java java hxsyl Ilovejava java JaVaAcmer"); 10 while(m.find()) { 11 System.out.println(m.group());//m.group会输出所有的java(忽略大小写) 12 13 } 14 15 16 String s = m.replaceAll("Java");//String也有该方法 17 System.out.println(s); 18 19 m.reset();//一定要加,因为find和matcher相互影响 20 StringBuffer sb = new StringBuffer(); 21 int i = 0; 22 /* 23 * 下面的方法是把找到的奇数个java替换为“Java”,偶数个替换成"java" 24 */ 25 while(m.find()) { 26 i++; 27 //不能直接写成i&1必须转化为boolean 28 if((i&1)==1) { 29 m.appendReplacement(sb, "Java"); 30 }else { 31 m.appendReplacement(sb, "java"); 32 } 33 } 34 35 m.appendTail(sb);//把找到的最后一个java后边的剩余字符串加上 36 System.out.println(sb);//不加reset的话只输出了Acmer 37 } 38 }
10.分组
1 /* 2 * 分别加上小括号,不算最外边的大括号,第一个左括号便是第一组 3 */ 4 Pattern p = Pattern.compile("(\\d{3,5})([a-z]{2})"); 5 String s = "123aaa-77878bb-646dd-00"; 6 Matcher m = p.matcher(s); 7 while(m.find()) { 8 System.out.println(m.group()); 9 System.out.println(m.group(1));//输出每对符合的 数字 10 System.out.println(m.group(2));//输出每对符合的 字母 11 }
11.抓取网页中的email
1 import java.io.BufferedReader; 2 import java.io.FileNotFoundException; 3 import java.io.FileReader; 4 import java.io.IOException; 5 import java.util.regex.Matcher; 6 import java.util.regex.Pattern; 7 8 /* 9 * 需要什么养的方法的话先些方法名 10 * 然后ctrl + 1列出推荐,系统创建该方法 11 */ 12 public class EmailSpider { 13 14 public static void main(String[] args) { 15 // TODO Auto-generated method stub 16 try { 17 BufferedReader br = new BufferedReader(new FileReader("F:\\regex.html")); 18 String line = ""; 19 try { 20 while((line=br.readLine())!=null) { 21 solve(line); 22 } 23 } catch (IOException e) { 24 // TODO Auto-generated catch block 25 e.printStackTrace(); 26 } 27 28 } catch (FileNotFoundException e) { 29 // TODO Auto-generated catch block 30 e.printStackTrace(); 31 } 32 33 34 } 35 36 private static void solve(String line) { 37 // TODO Auto-generated method stub 38 //正则表达式要是不满足相应功能的话不会出错,因为他是字符串 39 Pattern p = Pattern.compile("[\\w[.-]]+@[\\w[.-]]+\\.[\\w]+"); 40 Matcher m = p.matcher(line); 41 42 while(m.find()) { 43 System.out.println(m.group()); 44 } 45 46 } 47 48 }
12.代码统计
1 /* 2 * 统计代码里多少空行,注释行,程序行 3 * 实际上使用String里的startsWith和endsWith也行. 4 * 若是项目经理用的话还要统计每行的字符数是否以{;结尾,防止偷懒 5 */ 6 import java.io.BufferedReader; 7 import java.io.File; 8 import java.io.FileNotFoundException; 9 import java.io.FileReader; 10 import java.io.IOException; 11 12 public class CoderCount { 13 14 static long normalLines = 0; 15 static long commentLines = 0; 16 static long whiteLines = 0; 17 18 public static void main(String[] args) { 19 File f = new File("D:\\share\\src"); 20 File[] codeFiles = f.listFiles(); 21 for(File child : codeFiles){ 22 if(child.getName().matches(".*\\.java$")) { 23 solve(child); 24 } 25 } 26 27 System.out.println("normalLines:" + normalLines); 28 System.out.println("commentLines:" + commentLines); 29 System.out.println("whiteLines:" + whiteLines); 30 31 } 32 33 private static void solve(File f) { 34 BufferedReader br = null; 35 boolean comment = false; 36 try { 37 br = new BufferedReader(new FileReader(f)); 38 String line = ""; 39 while((line = br.readLine()) != null) { 40 /* 41 * //有的注释行前面有一个tab 42 * 不可写在readLine后 43 * 最后一行的话会空指针 44 */ 45 line = line.trim(); 46 //readLine读出字符串后就把后面的换行去掉啦 47 if(line.matches("^[\\s&&[^\\n]]*$")) { 48 whiteLines ++; 49 } else if (line.startsWith("/*") && !line.endsWith("*/")) { 50 commentLines ++; 51 comment = true; 52 } else if (line.startsWith("/*") && line.endsWith("*/")) { 53 commentLines ++; 54 } else if (true == comment) { 55 commentLines ++; 56 if(line.endsWith("*/")) { 57 comment = false; 58 } 59 } else if (line.startsWith("//")) { 60 commentLines ++; 61 } else { 62 normalLines ++; 63 } 64 } 65 } catch (FileNotFoundException e) { 66 e.printStackTrace(); 67 } catch (IOException e) { 68 e.printStackTrace(); 69 } finally { 70 if(br != null) { 71 try { 72 br.close(); 73 br = null; 74 } catch (IOException e) { 75 e.printStackTrace(); 76 } 77 } 78 } 79 } 80 81 }
13.Quantifiers
包括?*+;默认全是Greedy,还有Reluctant和Possessive(独占性的)。
1 //加上分组是为了看得更清晰一些 2 Pattern p = Pattern.compile("(.{3,10})+[0-9]"); 3 String s = "aaaa5bbbb6";//长度是10 4 Matcher m = p.matcher(s); 5 /* 6 * 现在输出0-10,默认是Greedy,先吞进10个字符,发现不匹配,吐出来一个,发现匹配了; 7 * 若是Pattern.compile("(.{3,10}?)+[0-9]")则成了Reluctant,那么是先吞进三个字符,发现不匹配,继续吞入 知道匹配,输出0到5; 8 * 若是Pattern.compile("(.{3,10}++)+[0-9]")则是Possessive(独占式),也是先吞入10个字符,但是不向外吐,那么就不匹配了, 9 * 这种方式主要用在需要高效率的地方(会有误差)。 10 */ 11 if(m.find()) { 12 System.out.println(m.start() + "----" + m.end()); 13 }else { 14 System.put.println("Not match!"); 15 }
14.补充(非捕获组)
这种方法比较绕,比较少用,掌握(?=a)的用法即可。
1 //非捕获组的意思和字面相反,意思是若是符合则捕获 2 Pattern p = Pattern.compile("(?=a).{3}"); 3 /* 4 * 输出a66,相当于要求以a开头,也可以这么写Pattern.compile("[a].{2}"); 5 * 若是Pattern.compile(".{3}(?!=a)")不是不以a结尾{2}[^a],而是下一个字符不是a(lookahead),输出44a,66b,所以这种用法不常用; 6 * 若是Pattern.compile(".{3}(?=a)")则输出444(因为?=a是lookahead),放在前面则包含在组内,后面则不包含在组内; 7 * 8 * 9 */ 10 String s = "444a66b"; 11 Matcher m = p.matcher(s); 12 while(m.find()) { 13 System.out.println(m.group()); 14 }
15.Back Reference
1 Pattern p = Pattern.compile("(\\d\\d)\\1"); 2 /* 3 * 输出true,\\1表示和第一个组的一样,若改成1213就不对了; 4 * 若是Pattern.compile("(\\d(\\d))\\2")则需改成122才对 5 * 6 */ 7 String s = "1212"; 8 Matcher m = p.matcher(s); 9 System.out.println(m.matches());
16.flags的简写
"."是不匹配换行的,记住CASE_INSENSITIVE就行了,简写“通过嵌入式标志表达式 (?i) 也可以启用不区分大小写的匹配”。
结束语:没必要专门看书,不会的话直接在网上找就好,别人问就一定要回答,正则表达式终于学完了,还是蛮有成就感的,哈哈……