-- Start
如果你还不知道什么是正则表达式或者不是很清楚,请先阅读 正则表达式精萃。
下面是一个在 Java 中使用正则表达式的简单例子。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test {
public static void main(String[] args) {
// Pattern 表示一个正则表达式
// Pattern 的构造方法是私有的
// 可以通过 compile 静态方法实例化 Pattern 对象
// 在实例化的同时我们可以不指定或指定一个或多个全局匹配模式
// 下面的代码构建了用来匹配固定电话号码的正则表达式, 采用不区分大小写和多行模式
Pattern p = Pattern.compile("(\\d{3}\\d?)-(\\d{7}\\d?)", Pattern.CASE_INSENSITIVE|Pattern.DOTALL);
// 将构建好的正则表达式与目标字符串关联得到一个 Matcher 对象
Matcher m = p.matcher("我的电话号码是 0411-88888888, 欢迎骚扰。");
// find() 方法将会在目标字符串中查找符合正则表达式的子字符串, 如果找到则返回 true
// 再次调用 find() 方法将会在上次匹配的结束位置开始查找
// find(int start) 方法将从参数指定的位置开始查找符合正则表达式的子字符串
if (m.find()) {
// start() 方法返回整个匹配的起始位置
// end() 方法返回整个匹配的结束位置
// group() 方法返回匹配的内容,
System.out.println("在目标字符串的 " + m.start() + " 到 " + m.end() + " 位置找到了 " + m.group());
// start(int group) 方法返回第group个捕获型括号匹配的起始位置
// end(int group) 方法返回第group个捕获型括号匹配的结束位置
// group(int group) 方法返回第group个捕获型括号匹配的内容
// groupCount() 返回捕获型括号的数目
System.out.println("在目标字符串的 " + m.start(1) + " 到 " + m.end(1) + " 位置找到了电话号码的区号 " + m.group(1));
System.out.println("在目标字符串的 " + m.start(2) + " 到 " + m.end(2) + " 位置找到了电话号码 " + m.group(2));
} else {
System.out.println("没有找到匹配的电话号码。");
}
// pattern() 方法返回当前 Matcher 使用的正则表达式
// usePattern 方法用来更改和当前 Matcher 匹配的正则表达式
m.usePattern(Pattern.compile("\\w+@[.\\w]+")); // 用来匹配邮件地址
// reset(CharSequence input) 方法用来更改和当前 Matcher 匹配的字符串
m.reset("wave0409@163.com");
// matches 方法用来验证目标字符串是否和正则表达式完全匹配
// 而 find 方法是去目标字符串中查找符合正则表达式的子字符串
// lookingAt 方法非常类似matches 方法,但不要求完全匹配
if(m.matches()) {
System.out.println(m.group() + " 是一个合法的 Email 地址。");
}
}
}
由此可见,在 Java 中使用正则表达式是非常简单的,我们只用到两个类 Pattern 和 Matcher。Pattern 对象表示一个正则表达式,在构建 Pattern 对象时,我们可以指定一个或多个全局匹配模式,Java 支持如下匹配模式。
全局匹配模式 | 局部匹配模式 | 描述 |
Pattern.UNIX_LINES | d | 只把\n作为换行符 |
Pattern.DOTALL | s | 点号能够匹配换行符 |
Pattern.MULTILINE | m | ^和$可以匹配字符串内部换行符 |
Pattern.COMMENTS | x | 注释模式 |
Pattern.CASE_INSENSITIVE | i | 对ASCII字符进行不区分大小写匹配 |
Pattern.UNICODE_CASE | u | 对Unicode字符进行不区分大小写匹配 |
Pattern.CANNON_EQ | 不同的编码中相同的字符视为相等 | |
Pattern.LITERAL | 纯文本模式 |
之后我们可以将正则表达式与目标字符串关联得到一个 Matcher 对象,利用 Matcher 对象提供的方法我们可以对字符串进行查找、替换等操作。
在上面的例子中,我们在整个目标字符串中查找能够匹配正则表达式的子字符串,事实上在应用正则表达式的时候,我们还可以指定目标字符串的检索范围,也就是说在目标字符串的子字符串中应用正则表达式。但此时会有一个问题,那就是 ^ 和 $ 应该匹配整个字符串的开头和结尾呢? 还是检索范围的起始和结束位置呢?Java 为我们提供了足够的灵活性,我们可以通过下面的方法来查看和设置,默认值是匹配检索范围的起始和结束位置。
hasAnchoringBounds()
useAnchoringBounds(boolean b)
还有一个问题是是否允许环视和单词分界符等跨越检索范围,同样,Java 为我们提供了下面的方法来查看和设置。默认情况下是不允许的。
hasTransparentBounds()
useTransparentBounds(boolean b)
下面是一个简单的例子。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test {
public static void main(String[] args) {
Pattern p = Pattern.compile("^\\d{3}\\d?-\\d{7}\\d?(?=,)");
Matcher m = p.matcher("我的电话号码是 0411-88888888, 欢迎骚扰。");
// region 方法设置正则表达式的检索范围
// regionStart() 方法返回检索范围的起始位置
// regionStart() 方法返回检索范围的结束位置
m.region(8, 21);
System.out.println("我们将在字符串的 " + m.regionStart() + " 到 " + m.regionEnd() + " 范围内应用正则表达式。");
// 设置环视可以突破检索范围,从而使表达式能够匹配成功
m.useTransparentBounds(true);
if (m.find()) {
System.out.println("在目标字符串的 " + m.start() + " 到 " + m.end() + " 位置找到了 " + m.group());
} else {
System.out.println("没有找到匹配的电话号码。");
}
// reset() 方法用来取消检索范围
m.reset();
}
}
上面我们介绍了如何进行查找操作, 下面我们通过一个例子看一下如何进行替换操作。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test {
public static void main(String[] args) {
Pattern p = Pattern.compile("^.+(\\d{4}-\\d{8}).*$");
Matcher m = p.matcher("我的电话号码是 0411-88888888, 欢迎骚扰。");
// replaceAll 和 replaceFirst 方法用来执行替换操作
// 下面的替换操作将把电话号码从目标字符串中提取出来
String myPhoneNumber = m.replaceAll("$1");
System.out.println("我的电话是:" + myPhoneNumber);
// 在替换操作中 $ 符号用来反向引用
// 如果要替换的内容是从变量中传人的,而变量中恰好有 $ 符号,这将导致替换失败
// 因此在替换之前最好使用 quoteReplacement 方法转义参数
String salary = "$1000";
m.usePattern(Pattern.compile("\\d+"));
m.reset("我的工资是 1000");
String result = m.replaceFirst(Matcher.quoteReplacement(salary));
System.out.println(result);
}
}
上面的例子演示了一个简单的替换操作,下面的例子演示了一个高级的替换操作。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test {
public static void main(String[] args) {
Pattern p = Pattern.compile("(\\d+)元");
Matcher m = p.matcher("我的工资是 1000元, 张三的工资是 1500元, 你的呢?");
StringBuffer result = new StringBuffer();
while(m.find()){
Double salary = Double.valueOf(m.group(1));
salary = salary / 6.3698; // 转换汇率
String replacement = String.format("%.2f美元", salary); // 保留2位小数
// 在每次 find 方法之后调用 appendReplacement
// 它将匹配之前的文本和要替换的内容添加到 result 中
// 第一次循环时,它将匹配之前的文本(我的工资是 )
// 和替换的内容(replacement)添加到 result 中
// 第二次循环时,它将匹配之前的文本(, 张三的工资是 )
// 和替换的内容(replacement)添加到 result 中
m.appendReplacement(result, replacement);
}
// 循环之后, appendTail 方法将剩余的文本添加到 result 中
m.appendTail(result);
// 打印结果
System.out.println(result);
}
}
上面两个关于替换的例子,我们并没有直接对原始字符串替换,而是返回替换后的结果,原始字符串并没有改变,如果原始字符串很长,这将严重影响性能,下面的例子演示了如何直接对原始字符串进行替换。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test {
public static void main(String[] args) {
StringBuilder text = new StringBuilder("我的工资是 1000元, 张三的工资是 1500元, 你的呢?");
Pattern p = Pattern.compile("(\\d+)元");
Matcher m = p.matcher(text);
int matchPoint = 0;
while (m.find(matchPoint)) {
matchPoint = m.end();
text.replace(m.start(), m.end(), String.format("%.2f美元", Double.valueOf(m.group(1)) / 6.3698));
}
System.out.println(text);
}
}
---更多参见:Java 精萃
-- 声 明:转载请注明出处
-- Last Updated on 2012-06-09
-- Written by ShangBo on 2012-06-04
-- End