概述:符合一定规则的表达式。用于专门用于操作字符串的。虽然字符串中有很多字符串的操作方法,但是使用起来比较繁琐,而且代码量比较多。
特点:用一些特定的符号表示以一些代码操作,简化书写,学习正则表达式就是学习一些特殊符号的使用。
好处:可以简化对字符串的复杂操作。
拿一个简单的例子比较说明:比如需要编写一个程序验证QQ号是否正确,首先QQ号必须是5到15位的,第一位1到9开头,后面可以使任意的数字。
使用传统操作字符串的方法:判断字符串长度是不是5到15,不满足则提示错误,满足在判断第一位是不是1到9,不满足提示错误,满足在循环遍历第二位以后字符是不是0到9的数字,不是则推出提示错误,满足则打印该QQ号。
class QQCheckDemo
{
public static void main(String[] args) {
String QQ = "75332414";
if(QQ.length()>=5 && QQ.length()<=15)
{
if (!(QQ.startsWith("0")))
{
char[] arr = QQ.toCharArray();
boolean flag = true;
for (int i=0;i<QQ.length() ;i++ )
{
//System.out.println(arr[i]);
if(!(arr[i]>='0' && arr[i]<='9'))
{
flag = false;
System.out.println("出现非法字符");
break;
}
}
if (flag)
{
System.out.println(QQ);
}
}
else
System.out.println("首位不能为0");
}
else
System.out.println("QQ位数出错");
}
}
分析发现以上代码还可以简化,当字符串长度满足后,可以通过将字符串转换成长整型的数,就可以省去判断第一位和遍历判断第二位以后是不是数字的情况了。
代码:
try
{
Long l = Long.parseLong(QQ);
System.out.println("QQ:" + l);
}
catch (Exception e)
{
System.out.println("出现非法字符");
}
二,但是尽管这样做还是很麻烦,正则表达式则提供了更简便的判断字符串是否符合我们的要求。下面使用正则表达式判断 QQ 号是否符合要求。
Class QQCheck {
public static void main(String[] args) {
String qq = "534312354";
String regex = "[1-9][0-9]{4,14}";
//regex就是正则表达式的意思,在定义一个规则,第一位是1-9之间的数,从第二位开始是0-9之间的数,一共4到14个,少于4个和多于14都不行
boolean flag = qq.matches(regex);
if (flag)
{
System.out.println("QQ号是:"qq);
}
else
System.out.println("qq错误,请检查");
}
}
1,匹配:
匹配就是判断某个字符串是否符合规定的要求,比如上面判断QQ号是不是符合要求,就属于匹配。匹配返回的结果是true或者false。
使用的方法:String类的 public boolean matches(String regex)
regex就是正则表达式,判断给定字符串是否符合该表达式。
需求:判断用户输入的手机号是不是符合要求。手机号格式13*********,15*********,18********
分析:手机号第一位是1开头,第二位只能是3或5或8,后面全部是数字,手机号是11为数的。
String tel = “13854745658”;
String regex = “1[358]\\d{9}”; //其中\d代表的是0到9的数字,也可以写成[0-9],因为windows下的反斜杠会被转义,所以在加一个反斜杠。
boolean b = tel.matches(regex);
匹配过程:返回的结果是true,过程是:先判断tel的第一个字符是1,判断第二个是不是3或5或8,是3,正确,所以判断第三位,是数字,在判断第四个是数字,在判断第五个是数字......如果任意一个不满足,则不再判断下一位,返回错误。
2,切割:
使用指定的正则表达式拆分给定的字符串,使用的方法是String类的split()方法
public String[] split(String regex)
注意:字符串拆分后返回的是一个字符串数组。
public class Demo
{
public static void main(String[] args) {
String str = "dsfghdfjk*dshgkj*hbj*";
String[] ss = str.split("\\*"); //需要使用两次转义字符
for(String s : ss) {
System.out.println(s);
}
}
}
举例:
(1)字符串:s = “D:\\abc\\def\\t.txt”,切割得到文件夹及目录。
String[] strs = s.split(“\\\\”);使用两个反斜杠作为转义字符。
(2)字符串:s = “sgjh sajgdskjg sdaghu e”;
需要按照多个空格进行对字符串的拆分,正则表达式为:String regex = “ +”
一个空格加上一个”+”号表示,一个或多个空格。
(3)字符串:s = “fgsdkkksdgdfiiigsdfhhsagdfkhghll”,按照叠词进行对字符串的切割。
多个空格需要用到组的概念,正则表达式:String regex= “(.)\\1+”
()就是组,可以让规则的结果被重用,点代表的是任意字符,组的出现都有编号,标号是从1开始的,使用已有的组可以通过 \n(n是组号)获取。
该表达式的获取过程是:判断第一个字符是不是字符,是,判断第二个是不是字符,是,判断是不是和第一个相等,是在判断第三个是不是和第二个相等,不是则从这个位置分割一次,在判断第四个......
3,替换:
替换是通过String类的replaceAll()方法。
public String replaceAll(String regex,String s)
将字符串的所有匹配regex的字符串替换成s。
需求:将字符串的超过3位的数字全部替换成#号。
public class Demo2
{
public static void main(String[] args) {
String str = "agsdf1535dsh453432653dfshg3546";
//正则表达式:\d代表0到9之间的数字,{3,}代表数字出现三次或三次以上
String regex = "\\d{3,}"";
str = str.replaceAll(regex,"#");
System.out.println(str);
}
}
举例:
给定的字符串中有叠词,现在要将重叠的字符替换成一个该字符。
String s = “asdgdddsdggsagggrrryrtusdfgfd”;
String regex = “(.)\\1+”;
s = s.replaceAll(regex,”$1”);
$是一个特殊符号,如果想将多个连续相同字符替换成指定该连续字符就要使用$符号。
4,获取。
四,正则表达式的获取
获取比较特殊,和匹配,切割,替换都不同。获取是将字符串中的复合规则的子串抽取出来。
步骤:
1,将正则表达式封装成对象。
2,让正则表达式和要操作的字符串关联。
3,关联后获取正则表达式的匹配引擎。
4,通过引擎对复合规则的子串进行操作,比如取出。
方法是首先获得Pattern类的对象,因为正则表达式必须首先被编译成此类的实例,然后通过Matcher类的matcher方法创建匹配器,Matcher对象可以与任意的字符串串序列匹配。
Pattern类没有构造函数,所以要通过静态方法获取该类对象。调用顺序是:
Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaab");
boolean b = m.matches();
其实String类的matchs方法,底层使用的就是Pattern类和Matcher类对象来完成的,只不过是String类对其进行了封装,使用起来比较方便,但是有局限性。
需求:需求:获取句子中的三个字母的单词,打印出结果。给定句子:nothing is impossible,never give up our dream.
import java.util.regex.*;
public class Test1
{
public static void main(String[] args) {
String str = " nothing is impossible,never give up our dream abc.";
String reg = "\\b[a-z]{3}\\b";
//将正则表达式封装成对象
Pattern p = Pattern.compile(reg);
//将正则表达式与要操作的字符串关联,获取匹配器
Matcher m = p.matcher(str);
//将规则作用在字符串
//boolean b = m.find();//这里find是查找三位字母的单词,直到找到第一个为止
//System.out.println(m.group());//获取匹配后的结果
//要想全部找到必须使用循环,可以将find方法看做是一个迭代器。
while(m.find()) {
System.out.println(m.group());
}
}
}
正则表达式:String reg = "\\b[a-z]{3}\\b";的含义是:a到z的字母出现三次,\b表示的是单词的边界,两边是边界。
五,正则表达式的几个练习
正则表达式在的解题思路是:
(1)如果只想知道该字符串是对或错,使用匹配。matches()
(2)如果要将字符串按照给定的规则进行拆分成多个子串,使用切割。split()
(3)如果要将已有的字符串变成另一个,则使用替换。replaceAll()
(4)要拿到符合要求的子串,使用获取。
1,练习一:校验邮箱是否正确。
分析:邮箱格式必须要有@符号,@左边是字母数字的组合,位数字5到20位之间,@右边挨着@的部分是字母或数字的组合(可以使纯数字或字母),它后面是.com,.cn,.edu之类的字符,所以可以将带点的那一部分封装成组,出现一次或多次。
public class Test1
{
public static void main(String[] args) {
String mail = "s334g@qq.com.cn";
mail = "13456@1.n";
//必须有@符号,它左边是数字或字母或数字字母组合,出现5到20次。
//它右边是字母数字或组合,出现一次或多次,它后面是.com之类的。出现一次或多次
String reg = "[a-zA-Z_0-9]{5,20}@[a-zA-Z_0-9]+(\\.[a-zA-Z]+)+";
//不精确的匹配,建议使用上面的正则表达式,比较精确
//reg = "\\w{5,20}@\\w+(\\.\\w+)+";//不同在于最后的部分,可以使数字或字母
if(mail.matches(reg))
System.out.println("邮箱是:" + mail);
else
System.out.println("邮箱格式错误!");
}
}
2,练习二:将给定的字符串变成一句正常的话。例如:我我我我我我=====要要要。。。。。进进进..........黑黑黑马马------学学,,,,,,,习习习。。。变成:我要进黑马学习
代码:
class Test2
{
public static void main(String[] args) {
String s = "我我我我我我=====要要要。。。。。进进进..........黑黑黑马马------学学,,,,,,,习习习。。。";
//首先要将中间的符号全部去掉
s = s.replaceAll("\\=","");
s = s.replaceAll("\\。","");
s = s.replaceAll("\\.","");
s = s.replaceAll("\\-","");
s = s.replaceAll("\\,","");
System.out.println(s);
//去掉重复文字
s = s.replaceAll("(.)\\1+","$1");//将出现的任意字符当做第一个组1是组的编号
System.out.println(s);
}
}
3,练习三:将IP地址按照地址段顺序进行排序,给定的地址段格式是:
192.168.0.2 185.64.51 192.120.75 27.152.52 64.28.125.0 192.168.5.4
分析:1,将每一个ip地址的每一段变成三位,因为最高是三位,如果不这样的话,自然排序会按照第一个数字排序,2.152.5.3就会排到192.168.1.0之后。正确方法应该用002.152.005.003与192.168.001.000比较才对。
思路:1,将每位ip的每段前面加两个0,
2,然后去掉每个ip每段后三位的前面一部分,也就是保证每段是三位数。
3,将字符串按照空格拆分,得到每个ip,添加到TreeSet集合,因为TreeSet集合有排序功能。
import java.util.*;
class Test3
{
public static void main(String[] args) {
String ip = "192.168.0.2 185.64.51 192.120.75 27.152.52 64.28.125.0 192.168.5.4";
String reg = "(\\d+)";//找到连续的数字,该规则要重用,所以封装成组
//将每段前面加上2个0
ip = ip.replaceAll(reg,"00$1");//在第一组前面加两个0
//System.out.println(ip);
//值保留每段的后三位
reg = "0*(\\d{3})";//开头是0出现零次或多次,然后是数字出现三次,将数字出现三次封装成组,重用
ip = ip.replaceAll(reg,"$1");//替换成组1,也就是数字出现三次
//System.out.println(ip);
//拆分ip,获得每个ip
String[] str = ip.split(" +");
//将每个ip存储到集合中
TreeSet<String> trees = new TreeSet<>();
for(String s : str) {
trees.add(s);
}
//迭代集合,就是按照大小排序后的结果
for(String st : trees) {
st = st.replaceAll("0*(\\d+)","$1");//第一位是0出现零次或多次,然后是数字,出现最少一次,将数字出现一次或多次封装成组
System.out.println(st);
}
}
}
六,综合应用。
需求:编写一个网络爬虫,从一个文本文件中读取所有的邮箱。
分析:使用流从一个文本文件中读取每行文本数据,然后获取Pattern对象,每读取一行字符串就获取一次匹配器,寻找该行字符串是不是有符合正则表达式的部分。知道每行字符串都匹配完结束循环。
import java.io.*;
import java.util.*;
import java.util.regex.*;
class Test4
{
public static void main(String[] args) throws Exception {
Scanner in = new Scanner(new FileInputStream("mail.txt"));
//邮箱的正则表达式
String reg = "[a-zA-Z_0-9]+@[a-zA-Z_0-9]+(\\.[a-zA-Z]+)+";
Pattern p = Pattern.compile(reg);
//循环匹配每行字符串
while(in.hasNextLine()) {
String line = in.nextLine();
//System.out.println(line);
Matcher m = p.matcher(line);
while(m.find()) {
System.out.println(m.group());
}
}
in.close();
}
}
如何获取一个网页上的所有邮箱地址呢?只需要将输入流换成网络上的流就可以了。使用URL类获取。
URL url = new URL(“http://127.0.0.1:8080/web/mail.html”);//获取本地主机的Tomcat服务器上的网页。
URLConnection con = url.openConnection();
Scanner in = new Scanner(con.getInputStream());