day25_正则表达式(match,split,get,replace)
1.正则表达式概述:
/* 正则表达式: 符合一定规则的表达式 作用:用于专门操作字符串 特点:可以简化对字符串的复杂操作 优点:用于一些特定的符号来表示一些代码操作.这样就简化书写 缺点:符号定义越多,正则越多,阅读性极差 四大功能: 1.如果想知道该字符串是否满足要求->匹配:matches/Matcher 2.想要将已有的字符串变成另一个字符串->替换:reaplaceAll 3.想要按照自定的方式将字符串变成多个字符串->切割,获取规则以外的子串:split 4.想要拿到符合需要的字符串子串,获取.获取符合规则的子串:Pattern,Matcher */
2.匹配:
匹配符合规则的QQ,未使用正则与使用正则对比:
package regex; class RegexDemo{ /* 检测QQ的合法性: 1.5~15位 2.不能以0开头 3.只能是数字 */ public static boolean checkQQ(String QQ){ if(!(QQ.length()>=5 && QQ.length()<=15)) return false;//满足以上条件,直接返回 else if(QQ.startsWith("0")) return false; else{ char[] arr=QQ.toCharArray(); for(char ch : arr) if(!(ch>='0'&& ch<='9')) return false; return true;//能够走到这一步说明数组中元素都符合要求 } /* //或者使用: try{ int qq=Integer.parseInt(QQ); } catch(NumberFormatException e){ return false; } return true; */ } /*利用正则表达式检测*/ public static boolean regexCheckQQ(String QQ){ String regex="[1-9][0-9]{4,14}";//第一位1~9,第二位0~9,出现了4~14次 return QQ.matches(regex);// boolean matches(String regex) //告知此字符串是否匹配给定的正则表达式。 } public static void main(String[] args){ if(checkQQ(args[0])) System.out.println(args[0]+"合法"); else System.out.println(args[0]+"非法"); if(regexCheckQQ(args[0])) System.out.println(args[0]+"合法"); else System.out.println(args[0]+"非法"); } } /* 由此看出: 第一种方法是String类中的方法的组合,代码过于复杂,没有使用 正则表达式简单 *//* x 字符 x \\ 反斜线字符 \0n 带有八进制值 0 的字符 n (0 <= n <= 7) \0nn 带有八进制值 0 的字符 nn (0 <= n <= 7) \0mnn 带有八进制值 0 的字符 mnn(0 <= m <= 3、0 <= n <= 7) \xhh 带有十六进制值 0x 的字符 hh \uhhhh 带有十六进制值 0x 的字符 hhhh \t 制表符 ('\u0009') \n 新行(换行)符 ('\u000A') \r 回车符 ('\u000D') \f 换页符 ('\u000C') \a 报警 (bell) 符 ('\u0007') \e 转义符 ('\u001B') \cx 对应于 x 的控制符 字符类: [abc] a或b 或 c(简单类) [^abc] 任何字符,除了 a、b 或 c(否定) [a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围) [a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集) [a-z&&[def]] d、e 或 f(交集) [a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](减去) [a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](减去) 预定义字符类 . 任何字符(与行结束符可能匹配也可能不匹配) \d 数字:[0-9] \D 非数字: [^0-9] \s 空白字符:[ \t\n\x0B\f\r] \S 非空白字符:[^\s] \w 单词字符:[a-zA-Z_0-9] (邮箱名) \W 非单词字符:[^\w] 注意:java中\后跟的字符会被转义,例如:如果想使用\d->\\d(使\做为普通字符) Greedy 数量词 X? X,一次或一次也没有 例如:"[a-zA-Z]\\d?":第二位是数字,出现一次或者一次没有 X* X,零次或多次 例如:"[a-zA-Z]\\d*":第二位是数字,数字出现0~多次. X+ X,一次或多次 X{n} X,恰好 n 次 X{n,} X,至少 n 次 X{n,m} X,至少 n 次,但是不超过 m 次 例如:QQ:"[1-9]\\d{4,14}" */匹配示例:
//使用规则匹配整个字符串:String中的matches方法,在匹配过程中一旦发生不匹配,返回false class RegexDemo2{ public static void demo(){ String str="a"; String regex="[bcd]";//字符串只能有一个字符,字符串中的第一个字符只能是b或c或d System.out.println(str.mathes("[bcd]")); } public static void main(String[] args){ demo() } } /* 手机号段: 13***,15***,18*** (共11位) regex:"1[358]\\d{9}" */
3.切割与替换:
package regex; import java.util.Arrays; class RegexDemo3{ public static void splitResult(String str,String regex){ String[] split=str.split(regex); System.out.println(split.length); for( String s : split) System.out.println(s); System.out.println(); } public static void splitDemo(){ String str="zhangsan wangwu lisi"; String regex=" +";//zhangsan,wangwu之间可能出现一次或多次空格 splitResult(str,regex); str="zhangsan.wangwu.lisi"; regex="\\.";//想按照.切割,在正则表达式中.代表任意字符(有特殊含义) //因此在正则中\.转义,但是又产生个问题,java中\.没有这个转义字符(报错:非法的转义字符,就好像\n:换行,\.没有特殊含义) //那么在使\做为普通字符,\\-->\\. splitResult(str,regex); str="c:\\abc\\1.txt"; regex="\\\\";//相当于两个普通的'\' splitResult(str,regex); str="erkktyqqquizzzzzo"; regex="(.)\\1+"; //+:出现一次或多次,也可以使用{1,} splitResult(str,regex); //按照叠词切割,为了可以让规则的结果被重用 //可以将规则封装成一个组,用()完成.组的出现都有编号 //从1开始.想要使用(捕获)已有的组可以通过\n(n就是组的编号)形式获取 //((A)(B(C))):4组,看组的个数从左括号的个数入手,并且从左到右依次编号:1组,2组...... } /*替换*/ public static void replaceAllDemo(){ String str="wer13899800000ty1234564uiod234uiod234345675f";//将字符串中连续5个或5个以上的数字替换成# String regex="\\d{5,}"; String newStr="#"; System.out.println(str.replaceAll(regex,newStr)); str="erkktyqqquizzzzzo";//将叠词替换成& regex="(.)\\1+"; newStr="&"; System.out.println(str.replaceAll(regex,newStr)); regex="(.)\\1+";//将叠词替换成单个字母:kk->k,qqq->q.... newStr="$1";//由于后面替换需要根据正则中的'.'来决定,获取上面regex中的第一组 System.out.println(str.replaceAll(regex,newStr)); newStr="$";//把叠词替换成$ System.out.println(str.replaceAll(regex,newStr));//Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 1 //这是因为$在正则中为特殊符号,IndexOutOfBoundsException - 如果替换字符 串引用模式中不存在的捕获组。 } //可以用"\\$"替换; public static void main(String[] args){ splitDemo(); System.out.println(); replaceAllDemo(); } }
4.获取:
/* 正则表达式: 4.获取功能:将字符串中符合规则的字串取出 操作步骤: 1.将正则表达式封装成对象. 2.让正则对象和要操作的字符串相关联 3.关联后,获取正则匹配引擎 4.通过引擎对符合规则的子串进行操作,比如取出 */ package regex; import java.util.regex.Pattern; import java.util.regex.Matcher; class RegexDemo4{ public static void getDemo(String str,String regex){ //将规则封装成对象 Pattern p=Pattern.compile(regex); //让正则对象和要操作的字符串相关联,获取匹配器对象 Matcher m=p.matcher(str);//Matcher 通过解释 Pattern 对 character sequence 执行匹配操作的引擎。 //System.out.println(m.group());//IllegalStateException - 如果没有尝试任何匹配,或者以前的匹配操作失败 //异常原因:只获取到了匹配器对象,而没有进行匹配查找动作,因此没有结果 //返回值:以前匹配操作所匹配的字符串形式的子序列(可能为空)。 /* m.find();//尝试查找与该模式匹配的输入序列的下一个子序列。 //当且仅当 输入序列的子序列(子串) 匹配此匹配器的模式(正则表达式) 时才返回 true System.out.println(m.group()+"\n");//返回第一次匹配的子串 */ while(m.find()){//类比Iterator,每调用一次find都从上个以匹配的子序列的下个位置开始 System.out.println(m.group());//jiu you //将会找出满足[a-z]{3}所有子串 System.out.println(m.start()+"..."+m.end());//输出当前匹配子串的起始位置(0开始)和结尾位置(包含头不包含尾) } } public static void main(String[] args){ getDemo("ming tian jiu you ","[a-z]{3}"); System.out.println(); //regex="\\b[a-z]{3}\\b";//\b 边界匹配器,单词的边界 getDemo("ming tian jiu you ","\\b[a-z]{3}\\b"); } }注意:
/* //分析以下结果: String str="ming tian jiu you "; String regex="\\b[a-z]{4}\\b"; Pattern p=Pattern.compile(regex); Matcher m=p.matcher(str); System.out.println(m.matches());//返回false,mathes会将整个字符串与模式匹配 //对于:\\b[a-z]{4}\\b 该规则限定长度为4的字符串,因此不匹配 while(m.find())//如果使用\\b[a-z]{4}\\b规则匹配 System.out.println(m.group());//理应结果是ming tian //但是只有tian,这是因为在调用m.mathes后,匹配失败,内部指针已指到t位置,从该位置在开始匹配 */
/* 转自:http://hi.baidu.com/bpy838/item/2eff9191500408f029164744(略有删改) 单词边界深入讨论: 1.到底什么是单词边界? 单词字符:\w 单词字符:[a-zA-Z_0-9]:a~z 或 A~Z 或 下划线 或 0~9 元字符<<\b>>也是一种对位置进行匹配的“锚”。这种匹配是0长度匹配。有4种位置被认为是“单词边界”: 1)在字符串的第一个字符前的位置(如果字符串的第一个字符是一个“单词字符”) 例如:"Txxxxxx",T前的void匹配边界 2)在字符串的最后一个字符后的位置(如果字符串的最后一个字符是一个“单词字符”)例如:"xxxxxm",m后的void匹配边界 3)在一个“单词字符”和“非单词字符”之间,其中“非单词字符”紧跟在“单词字符”之后 例如:"A bxxxx",A和空格之间认为匹配\b,然后从空格开始匹配 4)在一个“非单词字符”和“单词字符”之间,其中“单词字符”紧跟在“非单词字符”后面 例如:" Asxxxxx",空格和A之间匹配\b,然后从A开始匹配 例:This island is beautiful 正则表达式:\bis\b 1.T前void匹配\b,T不匹配i,失败 2.一直匹配,直到第四个s和空格之间匹配\b,空格不匹配i,失败 3.第五个空格和后面的i之间匹配\b,i匹配i,s匹配s,但是s后l不匹配\b,失败 4.一直匹配,直到d和空格之间匹配\b,空格不匹配i,失败 5.空格和i之间匹配\b,i匹配i,s匹配s,s和空格之间匹配\b,成功->is 接下来的步骤同理. */
5.正则练习:
package regex; import java.util.Arrays; class RegexTest{ public static void main(String[] args){ replaceAll(); System.out.println(); IPSplit(); System.out.println(); emailMatch(); } public static void replaceAll(){ String str="我我...我我...我要...要要...学学学...学学...编编编...编程..程.程程...程...程";//转换为"我要学编程" String regex="\\.+"; String newStr=str.replaceAll(regex,""); System.out.println(newStr); regex="(.)\\1+"; System.out.println(newStr.replaceAll(regex,"$1")); } /* 192.68.1.254 102.49.23.013 10.10.10.10 2.2.2.2 8.109.90.30 将ip地址进行地址段顺序的排序. */ public static void IPSplit(){ //1.将每一段前面统一补两个零 String ip="192.68.1.254 102.49.23.013 10.10.10.10 2.2.2.2 8.109.90.30"; System.out.println((ip=ip.replaceAll("(\\d+)","00$1"))); //2.将每一段替换成该段后三位: System.out.println((ip=ip.replaceAll("0*(\\d{3})","$1")));//对于002,相当于0出现零次,002为数字出现3次 //3.把每一个IP地址切割出来 String[] ipArr=ip.split(" +"); //4.按照字符的ASCII码从小到大排序(TreeSet/Arrays.sort/Collections.sort) Arrays.sort(ipArr); for(String s : ipArr) System.out.println(s.replaceAll("0*(\\d+)","$1"));//5.需要把每一段多余的零去掉 } public static void emailMatch(){ /* 邮箱匹配: abc12@sina.com abc_45@mab.com.cn abc_44@163.com abc34@sina.edu.com.cn */ //精确匹配: String email="abc_45@mab.com.cn"; String regex="[a-zA-Z_0-9]{6,}@[a-zA-Z0-9]+(\\.[a-zA-Z]{2,3}){1,3}";//有些对用户名有位数具体要求6~12位等等 //(\\.[a-zA-Z]+){1,3} ->.edu.com.cn System.out.println(email.matches(regex)); // email="abcdefghk"; // regex="([a-z])+"; // System.out.println(email.replaceAll(regex,"00$1"));//测试结果00k,$1代表最后一个匹配的值:k } }
6.网页爬虫:
/* 网页爬虫:根据需要获取(爬)网页信息 */ package regex; import java.io.*; import java.util.regex.*; import java.net.*; class Siders{ public static void getFromHtml()throws Exception{ URL url=new URL("http://127.0.0.1:8080/myweb/mail.html");//假设mail.html含有邮箱地址,mail.html放到Tomcat下的webapps/myweb文件夹下 URLConnection urlConn=url.openConnection();//连接成功需要开启Tomcat服务器 BufferedReader bufIn=new BufferedReader(new InputStreamReader(urlConn.getInputStream())); String line=null; String regex="\\w+@\\w+(\\.\\w+)+";//这里采用了非精确匹配 Pattern p=Pattern.compile(regex); while((line=bufIn.readLine())!=null){ Matcher m=p.matcher(line); while(m.find()) System.out.println(m.group()); } } public static void main(String[] args)throws Exception{ getFromHtml(); } } /*获取文件中需要信息原理同上*/mali.html中的邮箱前后带有空格,这是正则表达式不精确缘故