正则表达式基本内容及常用操作
在Java中的正则表达式,必须首先被编译为java.util.regex.Pattern的实例。然后,可将得到的模式用于创建Matcher对象,依照正则表达式,该对象可以与任意字符序列匹配。 执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式。
因此,典型的调用顺序是
Pattern p=Pattern.compile("a*b");
Matcher m=p.matcher("aaaaaab");
boolean b=m.matches();
在仅使用一次正则表达式时,可以方便地通过此类定义mathces方法。此方法编译表达式并在单个调用中将输入序列与其匹配。语句
boolean b=Pattern.matches("a*b","aaaaaab");
等效于上面的三个语句,尽管对于重复的匹配而言它效率不高,因为它不允许重用已编译的模式。
因此,典型的调用顺序是
Pattern p=Pattern.compile("a*b");
Matcher m=p.matcher("aaaaaab");
boolean b=m.matches();
在仅使用一次正则表达式时,可以方便地通过此类定义mathces方法。此方法编译表达式并在单个调用中将输入序列与其匹配。语句
boolean b=Pattern.matches("a*b","aaaaaab");
等效于上面的三个语句,尽管对于重复的匹配而言它效率不高,因为它不允许重用已编译的模式。
- 基本内容
- 字符类
字符 | 说明 |
[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除了bc,等同于[ad-z](减去) |
[a-z&&[^m-p]] | a到z而非m到p(减去) |
- 预定义字符
字符 | 说明 |
. | 任何字符 |
\d | 数字 |
\D | 非数字 |
\s | 任何空白字符 |
\S | 任何非空白字符 |
\w | 单词字符(字母,数字,下划线) |
\W | 非单词字符,等同于[^\w] |
- 边界匹配器
字符 | 说明 |
^ | 行的开始 |
$ | 行的结束 |
\b | 单词边界 |
\B | 非单词边界 |
\A | 输入开始 |
\Z | 输入的结尾,仅用于最后一个结束符(如果有) |
\z | 输入的结尾 |
\G | 上一个匹配的结尾 |
- 数量词
字符 | 说明 |
X? | 匹配X0到一次 |
X* | 匹配X0到多次 |
X+ | 匹配X一到多次 |
X{n} | 匹配X恰好n次 |
X{n,} | 匹配X至少n次 |
X{n,m} | 匹配Xn到m次 |
- 运算符
字符 | 说明 |
XY | X后跟Y |
X|Y | X或Y |
(X) | X,作为捕获组 |
- 引用
字符 | 说明 |
\n | 任何匹配的捕获组 |
- 反斜线,转义和引用
反斜线(“\”)用于引用转义构造,同时还用于引用其他将被解释为非转义构造的字符。因此,表达式\\表示单个反斜线,而\{与左括号匹配。在不表示转义构造的任何字母字符前使用反斜线都是错误的;它们是为将来扩展正则表达式语言保留的。可以在非字母字符前使用反斜线,不管该字符是否非转义构造的一部分。
根据Java Language Specification的要求,Java源代码的字符串中的反斜线被解释为Unicode转义或其它字符转义。因此必须在字符串字面值中使用两个反斜线,表示正则表达式受到保护,不被Java字节码编译器所编译。例如,当解释为正则表达式时,字符串字面值“\b”与单个退格字符匹配,而“\\b”与单词边界匹配。字符串字面值“\(hello\)”是非法的,将导致编译时错误,要与字符串(hello)匹配,必须使用字符串字面值“\\(hello\\)”;
- 字符类
字符类可以出现在其它字符类中,并且可以包含并集运算符和交集运算符。
字符类运算符的优先级如下(由高到低):
- 字面值转义 \X
- 分组 [...]
- 范围 a-z
- 并集 [a-e][i-u]
- 交集 [a-z&&[aeiou]]
- 组和捕获
捕获组可以通过从左到右计算其开括号来编号。例如在表达式((A)(B(C)))中,存在四个这样的组:
- ((A)(B(C)))
- \A
- (B(C))
- (C)
组零始终代表整个表达式。
之所以这样命名捕获组是因为在匹配中,保存了与这些组匹配的输入序列的每个子序列。捕获的子序列稍后可以通过Back引用在表达式中使用,也可以在匹配操作完成后从匹配器获取。
- 正则表达式对字符串的常见操作
- 匹配
/* *匹配手机号码 */ public static void function_demo1(){ String str="15839098373"; String regex="1[3578]\\d{9}";\\"\\d"可换成{0-9} boolean b=str.matches(regex); System.out.println(b); }
- 切割
切割实际上就是String的split方法,此方法接收一个正则表达式字符串作为参数 /* *切割以下字符串"a b c d e" */ public static void function_demo2() { String str="a b c d e"; String[] ch=str.split(" "); for (String string : ch) { System.out.println(string); } } 运行结果: a b c d e 情境一:但是当字符串中间的空格数不确定时,如"a b c d e",此时再用split直接使用空格进行切割便得不到预想的结果; 此时,可用" +"正则表达式进行切割;即str.split(" +"); 情境二:当字符串为“a.b.c.d.e”时,此时再用“.”进行切割将得不到任何结果,因为“.”在正则表达式中代表任何字符,可使用"\\."进行转义; 情境三:当字符串为“a###b@@@c$$d##e”或“attttttbmmmmcnnnn”时,该如何切割出abcde?先揭示一下正则表达式的写法:str.split("(.)\\1+"); 解释一下"(.)\\1+":“.”在正则表达式中代码任意字符,我们想要去除的是字符串中重复的字符即字符出现的次数多于一次;“(.)”代表一 个组,组中的“.”即任意字符,那么接下来就是要用正则表达式去让这个组能重复的出现。前面在讲捕获组的时候有说过每个字符串可以以左括号来区 分组的编号,组零代表整个表达式即“(.)”;前面在讲引用的时候“\n”代表所引用的组,那么在这个示例中“\1”代表引用的是组1的编号中的内容 即“.”;“+”代表出现一次或多次;综合起来这个正则表达式的意思就是连续出现一次或多次的字符;
- 替换
替换实际上用的是字符串的replaceAll方法,此方法接收两个参数,第一个参数为正则表达式,第二个参数为替换的字符串 /* *替换“attttbnnnnc”中连续出现的字符为e */ public static void function_demo4() { String str="attttbnnnnc"; String regex="(.)\\1+"; System.out.println(str.replaceAll(regex, "e")); } 情境一:把上面字符串中连续出现的字符替换为一个,即替换为“atbnc”,str.replaceAll(regex,"$1")即可,“$1”代表拿前面被替换组中的字符 情境二:把手机号码“15890875345”的中间四位用“*”隐藏掉 public static void function_demo5() { String string="15890987656"; String regex="(\\d{3})(\\d{4})(\\d{4})"; System.out.println(string.replaceAll(regex, "$1****$3")); } 情境三:把字符串中的所有圆括号(中,英,全角,半角)替换为“*” public static void function_demo3() { String str="a((((((((b(((((((c))))))))))))d()()0)e"; String regex="([\\(|(|\\)|)])"; System.out.println(str.replaceAll(regex, "*")); }
- 获取
因正则表达式会被编译为Pattern的实例,然后可用Pattern的matcher方法获取Matter对象,然后用Matter对象的实例方法进行相关的一些操作 /* *获取字符串中以三个字母组成的单词的字符串,“jin tian shi ge hao tian qi , xia le yi dian xiao yu” */ public static void function_demo6() { String str = "jin tian shi ge hao tian qi , xia le yi dian xiao yu"; String regex = "\\b[a-z]{3}\\b";// "\b"确定单词边界 Pattern p = Pattern.compile(regex);//获取编译的Pattern对象 Matcher m = p.matcher(str);//通过Pattern对象获取Matcher对象的实例 while (m.find()) {//查找与该模式匹配的输入序列的下一个子序列 System.out.println(m.group());//返回在以前匹配操作期间由给定组捕获的输入子序列 } }
练习
一,治疗口吃
/* *“我我我我我我我我我......不不不不不不不不不不不不不不不......是是是是是......药药药药药......神神神神神......” */ public static void function_exercise1(){ String str="我我我我我我我我我......不不不不不不不不不不不不不不不......是是是是是......药药药药药......神神神神神......"; str=str.replaceAll("\\.","");//先把字符串中的“.”去掉 String regex="(.)\\1+";//把字符串中的重复字体去除的正则表达式 System.out.println(str.replaceAll(regex,"$1")); }
二,对IP地址按照A类B类C类D类E类进行排序(不理解IP地址分类的可以去百度一下)
/* *"192.168.0.1 89.123.0.1 3.3.3.3 156.0.231.5"对IP地址字符串进行排序 *操作思路:IP地址是用点分十进制的形式由32位的二进制被划分成了四段,每段8位转成十进制而形成的,每段范围0-255 *为了用TreeSet对对象进行排序,所以需要对每个IP地址的每一段进行等位补充,比如地址中的某一段是一位数的补为三位 *数,然后放到TreeSeet中进行排序 */ public static void function_demo7() { String ip_str="192.168.0.1 89.123.0.1 3.3.3.3 156.0.231.5"; String[] ip_strArry=ip_str.split(" +");//把IP地址用空格打散成一个字符数组 TreeSet ts=new TreeSet<>();//用TreeSet对对象进行排序 System.out.println("排序前:"); for(String str:ip_strArry) { System.out.println(str); str=str.replaceAll("(\\d+)", "00$1");//把每一段的数值前补充两个零,此时192.168.0.1变成了00192.00168.000.001 //而3.3.3.3变成了003.003.003.003 str=str.replaceAll("0*(\\d{3})", "$1");//把IP地址变为五位的,如00192,前两位的零去除,此时每个IP地址的每一段都是三位数 ts.add(str);//放入TreeSet中,默认排序 } System.out.println("------------------"); System.out.println("排序后:"); for(String str:ts) { str=str.replaceAll("0*(\\d+)", "$1");//把003这样的段位的前两个零去除 System.out.println(str); } }
三,网络爬虫
/* *爬取网页上面的邮箱号 */ public static void function_demo8() throws IOException { URL url=new URL("http://localhost:63342/BoKeYuanComplieProject/regex.html");//需要抓取的网页地址 //BufferedReader br = new BufferedReader(new FileReader("C:\\regex.html"));//抓取的本地文件 BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream())); String regex="\\w+@\\w+\\.([a-zA-Z]{1,3}){1,2}";//邮箱 Pattern p=Pattern.compile(regex);//把正则编译为Pattern对象 List list=new ArrayList();//存放抓取后的邮箱信息 String line = null; while ((line = br.readLine()) != null) { Matcher m=p.matcher(line); if(m.find()) { list.add(m.group()); } } for(String str:list) { System.out.println(str); } }