------
Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
以前在公司上班的时候,因为工作上的需要,同事写了一个在网页上抓取数据的程序,然后放在公司的服务器,不停的运行,然后从别人的网站上抓取我们需要的数据,每隔3秒就访问一次网页,然后从新的网页上抓取数据。当时感觉很牛B,毕竟叫我弄,我是搞不定的,因为我不懂正则表达式。如今,咱也学习正则表达了,嘿嘿
![大笑](http://static.blog.csdn.net/xheditor/xheditor_emot/default/laugh.gif)
咱们先来了解一下正则的知识
正则表达式:符合一定规则的表达式
作用:用于专门操作字符串
特点:用于一些特定的符号来表示一些代码操作。这样就简化书写。所以学习正则表达式,就是在学习一些特殊符号的使用。
好处:可以简化对字符串的复杂操作。
弊端:符号定义越多,正则越长,阅读性越差。
作用:用于专门操作字符串
特点:用于一些特定的符号来表示一些代码操作。这样就简化书写。所以学习正则表达式,就是在学习一些特殊符号的使用。
好处:可以简化对字符串的复杂操作。
弊端:符号定义越多,正则越长,阅读性越差。
以我们现阶段学习的知识来检验一个QQ号是否合格(不是以0开头,长度为5-15位的数字)
public static void checkQQ()
{
String qq="23414243";
int len=qq.length();
if(len>=5&&len<=15)
{
if(!qq.startsWith("0"))
{
char[] arr=qq.toCharArray();
boolean flag=true;
for(int x=0;x<arr.length;x++)
{
if(!(arr[x]>='0' &&arr[x]<='9'))
{
flag=false;
break;
}
}
if(flag)
{
System.out.println("qq:"+qq);
}
else
{
System.out.println("qq号不符合规则");
}
}
}
}
用正则表达式如何做呢?
public static void method()
{
//不能以0开头,长度为5-15位的QQ号
String str="1236465";
String regex="[1-9]\\d{4,14}";
boolean flag=str.matches(regex);
System.out.println(flag);
}
哇哦,屌爆了有没有!!!只有四句!!!哦,regex这个变量的值不懂?下面就来了。。。
字符 | |
---|---|
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] |
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] |
\p{Alnum} | 字母数字字符:[\p{Alpha}\p{Digit}] |
\p{Punct} | 标点符号:!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ |
再来看[1-9]\\d{4,14}:表示第一位是由1~9等数字组成。从第二位开始都是数字,而且数字的长度是4-14个,加上首位,长度就是5~15个了。。。
正则表达式具体操作功能
1、匹配 : String matches() 用规则匹配整个字符串,只要有一处不符合规则,就匹配结束,返回false
/*
匹配手机号,11位
手机号段只有13xxx 15xxx 18xxx
*/
public static void method_1()
{
String tel="18707122014";
String telReg="1[358]\\d{9}";
System.out.println(tel.matches(telReg));
}
2、切割 String[] split(String regex)
class RegexDemo
{
public static void main(String[] args)
{
//使用.切割(单独的.代表任意字符,所以要加上转义字符,而在字符串中要使用转义字符要再加上\)
method_Split("zhangsan.lisi.wangwu.sunba","\\.");
method_Split("c:\\abc\\a.txt","\\\\");
/*
为了可以让规则的结果被重用,可以将规则封装成一个组,用()完成。组的出现
都有编号,从1开始,想要使用已有的组,可以通过\n (n就是组的编号)的形式来获取
*/
//按叠词切(其中的小括号里的表示一组,后面的1表示后面的和第一组内容一致,.代表任意字符)
method_Split("sdkkaseqqtryyfkaacgh","(.)\\1");
//按多个叠词切()
method_Split("sdkkaseqqqqqtryyyyyyyyyfkaacgh","(.)\\1+");
}
public static void method_Split(String str,String regex)
{
String[] arr=str.split(regex);
for(String s:arr)
{
System.out.println(s);
}
}
public static void method_Split1()
{
String str="zhangsan lisi wangwu zhaoliu";
//切割多个空格
String regex=" +";
String[] arr=str.split(regex);
for(String s:arr)
{
System.out.println(s);
}
}
}
以前,当我们遇到一个字符串由多个字符串组成,中间用一个空格隔开的时候,我们往往会通过split(" ")将这个字符串切割成一个字符串数组,但是当中间隔开的不是一个空格,而是多个空格时,我们就会抓狂了。。。。但是现在,可以用一个空格加一个"+"来表示多个空格,同样可以完成切割。是不是很方便?
当用于分隔的不是空格,而是小数点的时候,就用小数点分隔,但是,小数点在这里表示的是任意字符,而我们仅仅想把它作为小数点用,所以就用到了转义字符,而在字符串中,表示转义字符要用两个\\来表示。如上例
若是用\\隔开的咋办?那就是\\\\
注:为了可以让规则的结果被重用,可以将规则封装成一个组,用()完成。组的出现 都有编号,从1开始,想要使用已有的组,可以通过\n (n就是组的编号)的形式来获取。
"(.)\\1" 这个代表的意思是,()中的是一个组,代表的是任意字符,而后面的\\1表示使用编号为1的组的结果,并在一起就是两个相同的任意字符,就是叠词,"(.)\\1+",这里多了个"+"表示多个叠词
3、替换 String replaceAll()
class RegexDemo
{
public static void main(String[] args)
{
//将字符串中长度大于等于5的数字替换成#
method_Replace("asfdf13845218652asdfgkf23345aff","\\d{5,}","#");
//将字符串中的叠词替换成"&"
method_Replace("sdkkaseqqqqqtryyyyyyyyyfkaacgh","(.)\\1+","&");
//将字符串中的叠词替换成一个,如zzz-->z
//通过$来获取前一个规则中的第一个组
method_Replace("sdkkaseqqqqqtryyyyyyyyyfkaacgh","(.)\\1+","$1");
}
//替换
public static void method_Replace(String str,String reg,String newstr)
{
str=str.replaceAll(reg,newstr);
System.out.println(str);
}
}
正则表达式的第四个功能
获取:将字符串中符合规则的子串取出
操作步骤:
1、将正则表达式封装成对象
2、让正则对象和要操作的字符相关联
3、关联后,获取正则匹配引擎
4、通过引擎对符合规则的子串进行操作。如取出
1、将正则表达式封装成对象
2、让正则对象和要操作的字符相关联
3、关联后,获取正则匹配引擎
4、通过引擎对符合规则的子串进行操作。如取出
上面的步骤中涉及到了两个类 Pattern和Matcher
Pattern:正则表达式的编译表示形式。
Matcher:通过解释 Pattern 对 character sequence 执行匹配操作的引擎。通过调用模式的 matcher 方法从模式创建匹配器。
Matcher:通过解释 Pattern 对 character sequence 执行匹配操作的引擎。通过调用模式的 matcher 方法从模式创建匹配器。
具体使用:
import java.util.regex.*;
class RegexDemo2
{
public static void main(String[] args)
{
getDemo();
}
public static void getDemo()
{
String str="ming tian jiu yao fang jia le ,da jia";
System.out.println(str);
/*
其实String类中的matches方法。用的就是Pattern和Matcher对象来完成的。
只不过被String的方法封装后,用起来较为简单。但是功能却单一。
*/
//获取字符串中4个长度的单词
//\b表示单词边际
String reg="\\b[a-z]{4}\\b";
//将规则封装成对象
Pattern p=Pattern.compile(reg);
//让正则对象和要作用的字符串相关联。获取匹配器对象
Matcher m=p.matcher(str);
/*类似于迭代器*/
//boolean b=m.find(); //将规则作用到字符串,并进行符合规则的子串查找
//System.out.println(m.group());//用于获取匹配后结果。
while(m.find())
{
System.out.println(m.group());
System.out.println(m.start()+"...."+m.end());
}
}
}
结果:
来两个练习:
/*
练习
到底用四种功能中的哪一个呢?或者哪几个呢?
思路方式:
1、如果只想知道该字符串是否对错,使用匹配
2、想要将已有的字符串变成另一个字符串,替换
3、想要按照自定的方式将字符串变成多个字符串,切割。获取规则以外的子串
4、想要拿到符合需求的字符串子串,获取。获取符合规则的子串
*/
import java.util.*;
class RegexTest1
{
public static void main(String[] args)
{
Method_Test1();
Method_Test2();
checkMail();
}
/*
需求:对邮件地址进行校验
*/
public static void checkMail()
{
String mail="abc123@sina.com.cn";
String reg="[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+)+"; //较为精确的匹配
reg="\\w+@\\w+(\\.\\w+)+";//相对不太精确的匹配,因为 "1@1.1"也能匹配正确
sop(mail.matches(reg));
}
/*
192.168.1.254 102.49.23.013 10.10.10.10 2.2.2.2 8.109.90.30
将IP地址进行地址段顺序的排序
1、按照每一段需要的最多的0进行补齐,那么每一段就会至少保证有3位。
2、将每一段只保留3位。这样,所有的IP地址都是每一段3位
*/
public static void Method_Test2()
{
String ip="192.168.1.254 102.49.23.013 10.10.10.10 2.2.2.2 8.109.90.30";
ip=ip.replaceAll("(\\d+)","00$1"); //补齐,使每一段至少3位
sop(ip);
ip=ip.replaceAll("0*(\\d{3})","$1"); //去掉多余的0,使每一段为3位
sop(ip);
String[] arr=ip.split(" +"); //将字符串转换为数据
TreeSet<String> ts=new TreeSet<String>();
for(String s:arr) //将数组中的元素添加到TreeSet集合中排序
{
ts.add(s);
}
for(String s:ts)
{
sop(s.replaceAll("0*(\\d)","$1")); //将每一段中多余的0去掉,如002-->2
}
}
/*
将下列字符串转成:我要学编程
*/
public static void Method_Test1()
{
String str="我我...我我...我想要...要要...要要...学学学...学学..编编编..编程..程.程程...程...程";
/*
将已有字符串变成另一个字符串,使用替换功能
1、可以将.去掉
2、再将多个重复的内容变成单个内容
*/
String reg1="\\.+";
str=str.replaceAll(reg1,"");
sop(str);
String reg2="(.)\\1+";
str=str.replaceAll(reg2,"$1");
sop(str);
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
应用:网页爬虫
旁白:妈呀,终于到正餐了,这个例子就是我开头说的,在网页上抓取数据的应用,来,咱也搞起。。。。
/*
网页爬虫
*/
import java.io.*;
import java.util.regex.*;
import java.net.*;
class RegexTest2
{
public static void main(String[] args) throws Exception
{
getMail() ;
}
public static void getMail() throws Exception
{
//数据源,从哪里抓取数据
URL url=new URL("http://tieba.baidu.com/p/2986729518"); //从贴吧里一大堆求种的贴子里选了一个
URLConnection urlconn=url.openConnection();
//获取读取流
BufferedReader brIn=new BufferedReader(new InputStreamReader(urlconn.getInputStream()));
//将抓取的数据存入一个文件中
BufferedWriter bwOut=new BufferedWriter(new FileWriter("mailList.txt"));
String line=null;
String mailReg="[a-zA-Z0-9]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+)+";
//将正则表达式封装成对象
Pattern p=Pattern.compile(mailReg);
while((line=brIn.readLine())!=null)
{
//文本和引擎相关联
Matcher m=p.matcher(line);
while(m.find())
{
String str=m.group();
bwOut.write(str);
bwOut.newLine();
bwOut.flush();
System.out.println(str);
}
}
}
}
结果:
![大笑](http://static.blog.csdn.net/xheditor/xheditor_emot/default/laugh.gif)
其实还有是有点瑕疵的,上面的截图只能截取部分,看一下文件吧
看到圈红线部分没,这部分肯定是有问题的,毕竟人家发的是QQ邮箱,但是最后截取的去含有字母。。。。。
我们可以在获取的结果里作下判断,如果结果字符串含有@qq,则从数字部分开始。。。但是也有问题,像uff0c开头的还好,后面的就是QQ号了,但是u5b50这样开始的就不好弄了。。。咋办?
看了下网页源代码:
实际上邮箱应该是选中部分,但是结果却是把前面的u300也截取过来了。。。各位看官,有什么方法没?(待续。。。)