很久之前,正则表达式就已经整合到标准Unix工具集之中,例如sed和awk,以及程序设计语言之中了,例如Python和Perl(有些人认为正是正则表达促成了Perl的成功)。而在Java中,字符串操作还主要集中于String、StringBuffer和StringTokenizer类。与正则表达式相比较,它们只能提供相当简单的功能。
正则表达式是一种强大而灵活的文本处理工具。使用正则表达式,我们能够以编程的方式,构造复杂的文本模式,并对输入的字符申进行搜索。一旦找到了匹配这些模式的部分,你就能够随心所欲地对它们进行处理。初学正则表达式时,其语法是一个难点,但它确实是一种简洁、动态的语言。正则表达式提供了一种完全通用的方式,能够解决各种字符串处理相关的问题:匹配、选择、编辑以及验证。
一般来说,正则表达式就是以某种方式来描述字符串,因此你可以说:“如果一个字符串含有这些东西,那么它就是我正在找的东西。”例如,要找一个数字,它可能有一个负号在最前面,那你就写一个负号加上一个问号,就像这样:
-?
要描述一个整数,你可以说它有一位或多位阿拉伯数字。在正则表达式中,用\d表示一位
数字。如果在其他语言中使用过正则表达式,那你立刻就能发现Java对反斜线\的不同处理。
在其他语言中,八表示“我想要在正则表达式中插入一个普通的(字面上的)反斜线,请不要
给它任何特殊的意义。”而在Java中,N的意思是“我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义。”例如,如果你想表示一位数字,那么正则表达式应该是Nd,如显
你想插入一个普通的反斜线,则应该这样训。不过换行和制表符之类的东西只需使用单反
线:\n\t。
要表示“一个或多个之前的表达式”,应该使用+。所以,如果要表示“可能有一个负号,
后面跟着一位或多位数字”,可以这样:
-?\d+
应用正则表达式的最简单的途径,就是利用String类内建的功能。例如,你可以检查一个
String是否匹配如上所述的正则表达式:
//:strings/IntegerMatch . java
public class IntegerMatch {
public static void main( String[] args ){
System.out.println("-1234".matches ( "-?\\d+" ) );
System.out.println("5678".matches("-?\\d+"));
System.out.println("+911".matches("-?\\d+"));
System.out.println("+911".matches("(-|\\+)?\\d+) ;
}
}
/* Output
true
true
false
true
前两个字符串满足对应的正则表达式,匹配成功。第三个字符串开头有一个+,它也是一个合法
的整数,但与对应的正则表达式却不匹配。因此,我们的正则表达式应该描述为:“可能以一个
加号或减号开头”。在正则表达式中,括号有着将表达式分组的效果,而竖直线则表示或操作。
也就是:
(-|\+)?
这个正则表达式表示字符串的起始字符可能是一个一或+,或二者皆没有(因为后面跟着?修饰符)。
因为字符+在正则表达式中有特殊的意义,所以必须使用\将其转义,使之成为表达式中的一个普通字符。
splilt()
String类还自带了一个非常有用的正则表达式工具——**split()**方法,其功能是“将字符串从正则表达式匹配的地方切开。”
示例1:
public class SplitDemo {
public static String[] ss = new String[20];
public SplitDemo() {
String s = "The rain in Spain falls mainly in the plain.";
// 在每个空格字符处进行分解。
ss = s.split(" ");
}
public static void main(String[] args) {
SplitDemo demo = new SplitDemo();
for (int i = 0; i < ss.length; i++)
System.out.println(ss[i]);
}
}程序结果:
The
rain
in
Spain
falls
mainly
in
the
plain.
量词
量词描述了一个模式吸收输入文本的方式:
贪婪型:
量词总是贪梦的,除非有其他的选项被设置。含焚表达式会为所有可能的模式发
现尽可能多的匹配。导致此问题的一个典型理由就是假定我们的模式仅能匹配第一个可能
的字符组,如果它是贪婪的,那么它就会继续往下匹配。
勉强型:
用问号来指定,这个量词匹配满足模式所需的最少字符数。因此也称作懒情的、
最少匹配的、非贪婪的、或不贪婪的。
占有型:
目前,这种类型的量词只有在Java语言中才可用(在其他语言中不可用),并且写。也更高级,因此我们大概不会立刻用到它。当正则表达式被应用于字符串时,它会产生相当多的状态,以便在匹配失败时可以回溯。而“占有的”量词并不保存这些中间状态,因此它们可以防止回溯。它们常常用于防止正则表达式失控,因此可以使正则表达式执行起
来更有效。
贪婪型 | 勉强型 | 占有型 | 如何匹配 |
---|---|---|---|
X? | X?? | X?+ | 一个或零个X |
X* | X*? | X*+ | 零个或多个X |
X+ | X+? | X++ | 一个或多个X |
X{n} | X{n}?} | X{n}+ | 恰好n次X |
X{n,} | X{n,}? | X{n,}+ | 至少n次X |
X{n,m} | X{n,m}? | X{n,m}+ | X至少次,且不超过m次 |
应该非常清楚地意识到,表达式X通常必须要用圆括号括起来,以便它能够按照我们期望的
效果去执行。例如:
abc+
看起来它似乎应该匹配1个或多个abc序列,如果我们把它应用于输入字符串abcabcabc,则实际上会获得3个匹配。然而,这个表达式实际上表示的是:匹配ab,后面跟随1个或多个c。要表明匹配1个或多个完整的abc字符串,我们必须这样表示:
(abc)+
我们会发现,在使用正则表达式时很容易混治,因为它是一种在Java之上的新语言。
组(Groups)
组是用括号划分的正则表达式,可以根据组的编号来引用某个组。组号为0表示整个表达式,组号1表示被第一对括号括起的组,依此类推。因此,在下面这个表达式,
A(B©)D
中有三个组:组0是ABCD,组1是BC,组2是C。
Matcher对象提供了一系列方法,用以获取与组相关的信息:
public int groupCount()返回该匹配器的模式中的分组数目,第0组不包括在内。
public String group()返回前一次匹配操作(例如:find())的第0组(整个匹配)。
public String group(int i)返回在前一次匹配操作期间指定的组号,如果匹配成功,但是指定的组没有匹配输入字符串的任何部分,则将会返回null。
public int start(int group)返回在前一次匹配操作中寻找到的组的起始索引。
public int end(int group)返回在前一次匹配操作中寻找到的组的最后一个字符索引加一的值。
start()与end()
在匹配操作成功后,start()返回先前匹配的起始位置的索引,而end()返回所匹配的最后字符的索引加一的值。匹配操作失败之后(或先于一个正在进行的匹配操作去尝试)调用start()或end()将会产生IllegalStartException。
Pattern和Matcher
一般来说,比起功能有限的String类,我们更愿构造功能强大的正则表达式
Nava.util.regex包,然后用static Pattern.compile0方法来编译你的正则化更出即可。它会根据你的String类型的正则表达式生成一个Pattern对象。接下来,把你想要检索的字符串传入Pattern对象的matcher0方法。matcher()方法会生成一个Matcher对象,它有很多功能可用。例如,它的replaceAllo方法能将所有匹配的部分都替换成你传入的参数。
@Test
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.Test;
public class RegexTest {
public void test3() {
String str="google";
for(int i=0;i<str.length();i++) {
String s=String.valueOf(str.charAt(i));
Pattern p=Pattern.compile(s);
Matcher m=p.matcher(str);
int count=0;
while(m.find()) {
count++;
if(count>1) {
break;
}
}
if(count==1) {//这是找到的字符串,只匹配一次。
System.out.println(s);
break;
}
}
}
}
附:
find()可以在输入的任意位置定位正则表达式,而lookiingAt()和matches()只有在正则表达式与输入的最开始处就开始匹配时才会成功。matches()只有在整个输入都匹配的正则表达式时才会成功,lookingAt()只要输入第一部分匹配就会成功。
下面是常用的正则表达式范例及其相关练习:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.Test;
public class Regular_Expression {
/**
* 正则表达式的范例
*/
@Test
public void test1() {
String regex = "\\d{11}";
// \\d:匹配数字位数
String str = "12423423433";
System.out.println(str.matches(regex));
}
@Test
public void test2() {
String regex = "hello";
// 即你的字符串中要包含hello
String str = "aahello";
// 改规则为只匹配hello字符串,甚至大小写都要一样。
System.out.println(str.matches(regex));
}
@Test
public void test4() {
String regex = "[abcdefgh]ello";
// 匹配方括号中任意一个字符
String str = "hello";
System.out.println(str.matches(regex));
}
@Test
public void test5() {
String regex = "[a-zA-Z0-9]ello";
// 同理匹配方括号中的任意一个字符,横杠为区间。
String str = "2ello";
System.out.println(str.matches(regex));
}
/**
* "+" 加号匹配一次到多次
* "*" 星号匹配零次到多次
* "?" 问号匹配零次到一次
*/
@Test
public void test6() {
// String regex = "[a-zA-Z0-9]+ello";
// String regex = "[a-zA-Z0-9]*ello";
String regex = "[a-zA-Z0-9]?ello";
// 同理匹配方括号中的任意一个字符,且可以多次匹配,横杠表示区间的意思。
String str = "2ello";
System.out.println(str.matches(regex));
str = "aello";
System.out.println(str.matches(regex));
str = "Aello";
System.out.println(str.matches(regex));
str = "Ae@llo";
// 此处因为当中包含了不属于方括号中的字符,则返回false。
System.out.println(str.matches(regex));
}
/**
* 重复匹配次数:
* {3} 匹配三次
* {3,5} 匹配三到五次
* {3,} 匹配三次以上
*/
@Test
public void test7() {
// String regex = "[a-zA-Z0-9] {3}";
// 此处多了一个空格,表示空格重复三次及其以上。
String regex = "[a-zA-Z0-9]{3}";
// 表示空格重复三次
String str = "hel";
System.out.println(str.matches(regex));
str = "he";
System.out.println(str.matches(regex));
}
@Test
public void test8() {
String regex = "[a-zA-Z0-9]{3,5}";
// 表示方括号内字符,重复匹配三次到五次
String str = "hello";
System.out.println(str.matches(regex));
str = "helloo";
System.out.println(str.matches(regex));
}
@Test
public void test9() {
String regex = "[a-zA-Z0-9]{3,}";
// 表示方括号内的字符,重复匹配三次以上。
String str = "helloooo";
System.out.println(str.matches(regex));
str = "he";
System.out.println(str.matches(regex));
}
/**
* 排除某些符号
*/
@Test
public void test10() {
String regex = "[^0-9]{3,}";
// 表示除了方括号内的字符,重复匹配三次以上。
String str = "he1";
System.out.println(str.matches(regex));
str = "hel";//这里虽然是三个字符,但是不匹配,返回false。
System.out.println(str.matches(regex));
}
/**
* 简写:
* \d 匹配数字
* \D 是不匹配数字
* \w 匹配所有字符,相当于[a-zA-Z0-9]
* \W 不匹配所有字符,相当于[^a-zA-Z0-9]
* \s 匹配所有空格字符,如:空格、置表和换行
* \S 不匹配所有空格字符,如:空格、置表和换行
* . 匹配任意非空格字符
*/
@Test
public void test11() {
String regex = "\\d{3}";
// 表示重复匹配数字三次
String str = "032";
System.out.println(str.matches(regex));
str = "@el";
System.out.println(str.matches(regex));
}
@Test
public void test12() {
String regex = "\\D{3}";
// 表示重复不匹配数字三次
String str = "asd";
System.out.println(str.matches(regex));
str = "a@3";
System.out.println(str.matches(regex));
}
@Test
public void test13() {
String regex = "\\w{3}";
// 表示重复匹配所有字符三次
String str = "asd";
System.out.println(str.matches(regex));
str = "a@3";
System.out.println(str.matches(regex));
}
@Test
public void test14() {
String regex = "\\W{3}";
// 表示重复不匹配所有字符三次
String str = " ";
System.out.println(str.matches(regex));
str = "a@3";
System.out.println(str.matches(regex));
}
@Test
public void test15() {
String regex = "\\s{3}";
// 表示重复匹配所有空格字符三次
String str = " ";
System.out.println(str.matches(regex));
str = "a@3";
System.out.println(str.matches(regex));
}
@Test
public void test16() {
String regex = "\\S{3}";
// 表示重复不匹配所有空格字符三次
String str = " ";
System.out.println(str.matches(regex));
str = "a@3";
System.out.println(str.matches(regex));
}
@Test
public void test17() {
String regex = ".{3}";
// 表示重复匹配任意字符三次
String str = "&@$";
System.out.println(str.matches(regex));
str = "~|*";
System.out.println(str.matches(regex));
}
/**
* 转义符:
* \\ 代表一个\
* \/ 代表一个/
* \[ 匹配一个[
* \] 匹配一个]
* \. 匹配一个.
* \+ 匹配一个+
* \? 匹配一个?
* \* 匹配一个*
* \{ 匹配一个{
* \} 匹配一个}
* ......
*/
@Test
public void test18() {
String regex="\\\\\\/\\[\\]\\.";
String str="\\/[].";
System.out.println(str.matches(regex));
}
/**
* 分组和或
*/
@Test
public void test19() {
String regex="(\\(0\\d{2}\\)|0\\d{2})( |\\-|_)\\d{7,8}";
String str="(029)-2342332";
System.out.println(str.matches(regex));
str="029-2342332";
System.out.println(str.matches(regex));
str="029 2342332";
System.out.println(str.matches(regex));
str="029_2342332";
System.out.println(str.matches(regex));
}
/**
* 贪婪匹配
*/
@Test
public void test20() {
String regex="%.+%";
String str="7adafafafd% %kjhjk% %tryuui%";
System.out.println(str.replaceAll(regex, "aaaa"));
}
/**
* 非贪婪匹配
*/
@Test
public void test21() {
String regex="%.+?%";//非贪婪匹配从前往后进行
String str="%7adafafafd% %kjhjk% %tryuui%";
System.out.println(str.replaceAll(regex, "aaaa"));
}
/**
* 分组匹配
*/
@Test
public void test22() {
String regex="(%)(.*?)(%)";
//整个字符串为组0,
//然后依次从左开始数,
//小括号包括的部分已依次为组1、组2和组3。
String str="%7adafafafd% %kjhjk% %tryuui%";
System.out.println(str.replaceAll(regex, "$2"));
//分好组的字符,通过$符加上组号,则就将此组号的内容取出。
System.out.println(str.replaceAll(regex, "$1aaaa$3"));
//同上,将所选择组号里的内容替换掉。
}
/**
* 解析对象
*/
@Test
public void test23() {
Pattern pattern=Pattern.compile("(%)(.*?)(%)");
//pattern是模式对象,此方法会将匹配到的东西遍历出来。
String str="%7adafafafd% %kjhjk% %tryuui%";
Matcher m=pattern.matcher(str);
int i=0;
while(m.find()) {//find为从匹配的对象里面去查找
i++;
// System.out.println(m.group(1)+"aaaa"+m.group(3)+" starIndex:"+m.start()+" endIndex:"+m.end());
//此处输入的为所替换的组号,而将该组内的内容进行替换。
System.out.println(m.group(2).substring(m.group(2).indexOf("7ad"))+" starIndex:"+m.start()+" endIndex:"+m.end());
//此处输入的为所替换的组号,并取出该组内的指定索引的内容。
}
}
}
import org.junit.Test;
public class RegexTest {
/**
* 测试电子邮件规则
* 1、包含@符
* 2、@符前有一个以上的单词字符
* 3、@符后要有一个.符号
* 4、@符合.符号之间要包含一个到多个单词字符
* 5、.符号后要包含一个到多个单词字符
* 6、后面还可能重复出现若干.单词字符
* 例:aaa@sdf.com.cn 345345@qq.com.cn.cc
*/
@Test
public void test1() {
String regex="\\w+@\\w+(\\.\\w+)+";
String str1="aaa@sdf.com.cn";
System.out.println(str1.matches(regex));
String str2="345345@qq.com.cn.cc";
System.out.println(str2.matches(regex));
}
@Test
public void test2() {
String str="google";
for(int i=0;i<str.length();i++) {
//字符数组的遍利
int count=0;
char a=str.charAt(i);
for(int j=0;j<str.length();j++) {
if(a==str.charAt(j)) {
count++;
if(count>1) {
break;
}
}
if(count==1) {
System.out.print(a);
break;
}
}
}
}
}
import java.util.Scanner;
import org.junit.Test;
public class 正则表达式练习 {
@Test
public void test1() {
String str = "Hello_World_!|Hi-Every,One.";
StringBuffer sb = new StringBuffer();
sb.append("_");
System.out.println(str.replace("_", " "));
System.out.println(str.replaceAll("_|\\||-|\\,", " "));
}
// /* 这是一个主函数 */
// void main() {
// int a = 10;// 声明一个变量a
// int b = 20;// 声明一个变量b
// printf("a+b=%d", a + b);
// }
@Test
public void test2() {
// Scanner s = new Scanner(System.in);
// StringBuffer sb = new StringBuffer();
// String temp;
// while(!(temp=s.nextLine()).equals("quit")) {
// sb.append(temp).append("\n");
// }
// String str=sb.toString();
StringBuffer sb = new StringBuffer();
sb.append("/*这是一个主函数*/\n")
.append("void main(){\n")
.append("int a=10;//声明一个变量\nn")
.append("int b=20;//声明一个变量b\n")
.append("printf(\"a+b=%d\",a+b);\n")
.append(" }\n");
String str = sb.toString();
String regex1 = "\\/\\*.+?\\*\\/";
str = str.replaceAll(regex1, "");
regex1 = "\\/\\/.+?\\\n";
System.out.println(str.replaceAll(regex1, "\n"));
}
}
该代码的执行结果:
Hello World !|Hi-Every,One.
Hello World ! Hi Every One.
void main(){
int a=10;
nint b=20;
printf("a+b=%d",a+b);
}