《疯狂java讲义》学习(25):处理日期的类
正则表达式
正则表达式是强大的字符串处理工具,可以对字符串进行查找、提取、分割、替换等操作。String类里也提供了几个特殊的方法:
- boolean matches(String regex):判断该字符串是否匹配指定的正则表达式。
- String replaceAll(String regex, String replacement):将该字符串中所有匹配regex的子串替换成replacement。
- String replaceFirst(String regex, String replacement):将该字符串中第一个匹配regex的子串替换成replacement。
- String[] split(String regex):以regex作为分隔符,把该字符串分割成多个子串。
上面这些特殊的方法都依赖于Java提供的正则表达式支持,除此之外,Java还提供了Pattern和Matcher两个类专门用于提供正则表达式支持。
创建正则表达式
正则表达式就是用于匹配字符串的模板,可以匹配一批字符串,所以创建正则表达式就是创建一个特殊的字符串,由合法字符和特殊字符组成:
合法字符详细:
特殊字符详细:
将上面多个字符组合使用即可创建正则表达式:
"\\u0041\\" //匹配a\
"\\0101\t" //匹配a<制表符>
"\?\[" //匹配?[
上面的正则表达式依然只能匹配单个字符,这是因为还未在正则表达式中使用“通配符”,“通配符”可以匹配多个字符,这里我们将其称为预定于字符:
上面的7个预定义字符其实很容易记忆——d是digit的意思,代表数字;s是space的意思,代表空白;w是word的意思,代表单词。d、s、w的大写形式恰好匹配与之相反的字符。
有了预定义字符后,就可以更方便的创建正则表到式:
c\wt //可以匹配cat、cbt、cct、c0t、c9t等一批字符串
\d\d\d-\d\d\d-\d\d\d\d //匹配如000-000-0000形式的电话号码
在一些特殊情况下,例如,若想匹配a~f的字母,或者匹配除了ab之外的所有小写字母,或者匹配中文字符,这里我们可以使用方括号表达式:
正则表示还支持圆括号表达式,用于将多个表达式组成一个子表达式,圆括号中可以使用或运算符(|)。例如,正则表达式"(public|protected|private)"用于匹配Java的三个访问控制符其中之一。
正则还支持对头尾的匹配,使用边界匹配符:
正则表达式还提供了数量标识符,正则表达式支持的数量标识符有如下几种模式。
- Greedy(贪婪模式):数量表示符默认采用贪婪模式,除非另有表示。贪婪模式的表达式会一直匹配下去,直到无法匹配为止。如果你发现表达式匹配的结果与预期的不符,很有可能是因为——你以为表达式只会匹配前面几个字符,而实际上它是贪婪模式,所以会一直匹配下去。
- Reluctant(勉强模式):用问号后缀(?)表示,它只会匹配最少的字符。也称为最小匹配模式。
- Possessive(占有模式):用加号后缀(+)表示,目前只有Java支持占有模式,通常比较少用
三种模式数量表示符:
关于贪婪模式和勉强模式的对比,看这个例子:
String str="hello , java!";
//贪婪模式的正则表达式
System.out.println(str.replaceFirst("\\w*" , "■")); //输出■ , java!
//勉强模式的正则表达式
System.out.println(str.replaceFirst("\\w*?" , "■")); //输出■hello , java!
正则表达式的使用
一旦在程序中定义了正则表达式,就可以使用Pattern和Matcher来使用正则表达式。
Pattern对象时正则表达式编译后的内存中的表示形式,因此,正则表达式字符串必须先被编译成为Pattern对象,然后在利用该Pattern对象创建对应的Matcher对象。执行匹配所涉及的状态保留在Matcher对象中,多个Matcher对象可共享同一个Pattern对象。
因此,典型的调用顺序如下:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PatternTest {
public static void main(String[] args){
//将一个字符串编译成Pattern对象
Pattern p=Pattern.compile("a*b");
// 使用Pattern对象创建Matcher对象
Matcher m=p.matcher("aaaaab");
boolean b=m.matches();
//返回true
System.out.println(b);
}
}
上面定义的Pattern对象可以多次重复使用。如果某个正则表达式仅需一次使用,则可直接使用Pattern类的静态matches方法,此方法自动把指定字符串编译成匿名的Pattern对象,并执行匹配,如下:
boolean b=Pattern.matches("a*b", "aaaaab"); //返回true
上面语句等效于前面的三条语句。但采用这种语句每次都需要重新编译新的Pattern对象,不能重复利用已编译的Pattern对象,所以效率不高。
Pattern是不可变类,可供多个并发线程安全使用。
Matcher类提供了如下几个常用方法。
- find():返回目标字符串中是否包含与Pattern匹配的子串。
- group():返回上一次与Pattern匹配的子串。
- start():返回上一次与Pattern匹配的子串在目标字符串中的开始位置。
- end():返回上一次与Pattern匹配的子串在目标字符串中的结束位置加1。
- lookingAt():返回目标字符串前面部分与Pattern是否匹配。
- matches():返回整个目标字符串与Pattern是否匹配。
- reset(),将现有的Matcher对象应用于一个新的字符序列。
在Pattern、Matcher类的介绍中经常会看到一个CharSequence接口,该接口代表一个字符序列,其中CharBuffer、String、StringBuffer、StringBuilder都是它的实现类。简单地说,CharSequence代表一个各种表示形式的字符串。
通过Matcher类的find()和group()方法可以从目标字符串中依次取出特定子串(匹配正则表达式的子串),下面的例子程序示范了这种用途:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FindGroup {
public static void main(String[] args){
//创建一个Pattern对象,并用它建立一个Matcher对象
Matcher m= Pattern.compile("\\w+").matcher("Java is very easy!");
while (m.find()){
System.out.println(m.group());
}
int i=0;
while (m.find(i)){
System.out.println(m.group() + "\t");
i++;
}
}
}
下面是运行结果:
Java
is
very
easy
Java ava va a is is s very very ery ry y easy easy asy sy y
从上面运行结果可以看出,find()方法依次查找字符串中与Pattern匹配的子串,一旦找到对应的子串,下次调用find()方法时将接着向下查找。除此之外,find()方法还可以传入一个int类型的参数,带int参数的find()方法从该int索引处向下搜索。
start()和end()方法主要用于确定子串在目标字符串中的位置,如下程序所示:
import java.util.regex.*;
public class StartEnd {
public static void main(String[] args){
//创建一个Pattern对象,并用它建立一个Matcher对象
String regStr="Java is very easy!";
System.out.println("目标字符串是:" + regStr);
Matcher m=Pattern.compile("\\w+").matcher(regStr);
while (m.find()){
System.out.println(m.group()+"子串的其实位置:"+m.start()+",其结束位置:"+m.end());
}
}
}
matcher()和lookingAt()方法有点相似,只是matcher()方法要求整个字符串和Pattern完全匹配时才返回true,而lookingAt()只要字符串以Pattern开头就会返回true。reset()方法可将现有的Matcher对象应用于新的字符序列。看如下例子:
import java.util.regex.*;
public class MatchesTest
{
public static void main(String[] args)
{
String[] mails=
{
"[email protected]" ,
"[email protected]",
"[email protected]",
"[email protected]"
};
String mailRegEx="\\w{3,20}@\\w+\\.(com|org|cn|net|gov)";
Pattern mailPattern=Pattern.compile(mailRegEx);
Matcher matcher=null;
for (String mail : mails)
{
if (matcher==null)
{
matcher=mailPattern.matcher(mail);
}
else
{
matcher.reset(mail);
}
String result=mail + (matcher.matches() ? "是" : "不是")
+ "一个有效的邮件地址!";
System.out.println(result);
}
}
}
上面程序创建了一个邮件地址的Pattern,接着用这个Pattern与多个邮件地址进行匹配。当程序中的Matcher为null时,程序调用matcher()方法来创建一个Matcher对象,一旦Matcher对象被创建,程序就调用Matcher的reset()方法将该Matcher应用于新的字符序列。
事实上,String类里也提供了matches()方法,该方法返回该字符串是否匹配指定的正则表达式。例如:
"[email protected]".matches("\\w{3,20}@\\w+\\.(com|org|cn|net|gov)")
//返回true
除此之外,还可以利用正则表达式进行对目标字符串的分割、查找、替换等操作,看如下例子:
import java.util.regex.*;
public class ReplaceTest {
public static void main(String[] args)
{
String[] msgs=
{
"Java has regular expressions in 1.4",
"regular expressions now expressing in Java",
"Java represses oracular expressions"
};
Pattern p=Pattern.compile("re\\w*");
Matcher matcher=null;
for (int i=0 ; i < msgs.length ; i++)
{
if (matcher==null)
{
matcher=p.matcher(msgs[i]);
}
else
{
matcher.reset(msgs[i]);
}
System.out.println(matcher.replaceAll("哈哈:)"));
System.out.println(matcher.replaceFirst("666"));
}
}
}
还可以用来查找,例如邮编等:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FindZipCode {
public static Pattern ZIP_CODE_PATTERN = Pattern.compile("(?<![0-9])[0-9]{6}(?![0-9])");
public static void findZipCode(String text) {
Matcher matcher = ZIP_CODE_PATTERN.matcher(text);
while (matcher.find()) {
System.out.println(matcher.group());
}
}
public static void main(String[] args) {
findZipCode("邮编 100013,电话18612345678");
}
}
java实例练习
E-mail格式合法性验证与自动修改
如何检验输入的E-mail格式是否合法,这在表单中运用得非常多。我们可以使用正则表达式来实现这一功能,并将其中错误的字符删除以产生合格的E-mail格式。
1.
新建项目checkEmail,并在其中创建一个checkEmail.java文件。在该类的主方法中使用Mather语句调用Pattern类的matcher()方法生成一个Matcher对象m,最后通过m.appendReplacement()方法实现自动修改功:
package chechEmail;
import java.util.regex.*;
public class chechEmail {
public static void main(String[] args){
String str="apple:[email protected]";
Pattern p=Pattern.compile("^\\.|^\\@"); //检测输入的Email地址是否以非法符号"."或"@"开头
Matcher m=p.matcher(str);
if (m.find()){
System.out.println("EMAIL地址不能以'.'或'@'作为起始字符");
}
p = Pattern.compile("^www\\."); // 检测是否以“www.”为起始
m = p.matcher(str);
if (m.find()) {
System.out.println("EMAIL地址不能以'www.'起始");
}
p = Pattern.compile("[^A-Za-z0-9\\.\\@_\\-~#]+");// 检测是否包含非法字符
m = p.matcher(str);
StringBuffer sb = new StringBuffer();
boolean result = m.find();
boolean deletedIllegalChars = false;
while (result) {
deletedIllegalChars = true; // 如果找到了非法字符那么就设下标记
// 如果里面包含非法字符如冒号双引号等,那么就把他们消去,加到SB里面
m.appendReplacement(sb, "");
result = m.find();
} m.appendTail(sb);
String str1 = sb.toString();
if (deletedIllegalChars) {
System.out.println("输入的EMAIL地址里包含有冒号、逗号等非法字符,请修改");
System.out.println("您现在的输入为: " + str);
System.out.println("修改后合法的地址应类似: " + str1);
}
}
}