天天看点

String.replaceAll() 正则表达式

最近项目中出现了一个正则表达式的BUG!

我用的是String.replaceAll()方法

查询发现它应该是调用了Matcher.replaceAll(),String.replaceAll()与下面这段代码等价

Pattern . compile ( regex ).matcher( str ).replaceAll( replacement )

所以在匹配字符为*或者+的时候!出错!

解决方法

附查阅的一些资料:

正则表达式是一个查询的字符串,它包含一般的字符和一些特殊的字符,特殊字符可以扩展查找字符串的能力,正则表达式在查找和替换字符串的作用不可忽视,它能很好提高工作效率。

EditPlus的查找,替换,文件中查找支持以下的正则表达式:

表达式 说明

\t 制表符.

\n 新行.

. 匹配任意字符.

| 匹配表达式左边和右边的字符. 例如, "ab|bc" 匹配 "ab" 或者 "bc".

[] 匹配列表之中的任何单个字符. 例如, "[ab]" 匹配 "a" 或者 "b". "[0-9]" 匹配任意数字.

[^] 匹配列表之外的任何单个字符. 例如, "[^ab]" 匹配 "a" 和 "b" 以外的字符. "[^0-9]" 匹配任意非数字字符.

* 其左边的字符被匹配任意次(0次,或者多次). 例如 "be*" 匹配 "b", "be" 或者 "bee".

+ 其左边的字符被匹配至少一次(1次,或者多次). 例如 "be+" 匹配 "be" 或者 "bee" 但是不匹配 "b".

? 其左边的字符被匹配0次或者1次. 例如 "be?" 匹配 "b" 或者 "be" 但是不匹配 "bee".

^ 其右边的表达式被匹配在一行的开始. 例如 "^A" 仅仅匹配以 "A" 开头的行.

$Content$nbsp;其左边的表达式被匹配在一行的结尾. 例如 "e$" 仅仅匹配以 "e" 结尾的行.

() 影响表达式匹配的顺序,并且用作表达式的分组标记.

\ 转义字符. 如果你要使用 "\" 本身, 则应该使用 "\\".

例子:

原始串

str[1]abc[991];

str[2]abc[992];

str[11]abc[993];

str[22]abc[994];

str[111]abc[995];

str[222]abc[996];

str[1111]abc[997];

str[2222]abc[999];

目标串:

abc[1];

abc[2];

abc[11];

abc[22];

abc[111];

abc[222];

abc[1111];

abc[2222];

处理:

查找串:str\[([0-9]+)\]abc\[[0-9]+\]

替换串:abc[\1]

【1】正则表达式应用——替换指定内容到行尾

原始文本如下面两行

abc aaaaa

123 abc 444

希望每次遇到“abc”,则替换“abc”以及其后到行尾的内容为“abc efg”

即上面的文本最终替换为:

abc efg

123 abc efg

解决:

① 在替换对话框,查找内容里输入“abc.*”

② 同时勾选“正则表达式”复选框,然后点击“全部替换”按钮

其中,符号的含义如下:

“.” =匹配任意字符

“*” =匹配0次或更多

注意:其实就是正则表达式替换,这里只是把一些曾经提出的问题加以整理,单纯从正则表达式本身来说,就可以引申出成千上万种特例。

【2】正则表达式应用——数字替换

希望把

asdadas123asdasdas456asdasdasd789asdasd

替换为:

asdadas[123]asdasdas[456]asdasdasd[789]asdasd

在替换对话框里面,勾选“正则表达式”复选框;

在查找内容里面输入“[0-9][0-9][0-9]”,不含引号

“替换为:”里面输入“[\0\1\2]”,不含引号

范围为你所操作的范围,然后选择替换即可。

实际上这也是正则表达式的使用特例,“[0-9]”表示匹配0~9之间的任何特例,同样“[a-z]”就表示匹配a~z之间的任何特例

上面重复使用了“[0-9]”,表示连续出现的三个数字

“\0”代表第一个“[0-9]”对应的原型,“\1”代表第二个“[0-9]”对应的原型,依此类推

“[”、“]”为单纯的字符,表示添加“[”或“]”,如果输入“其它\0\1\2其它”,则替换结果为:

asdadas其它123其它asdasdas其它456其它asdasdasd其它789其它asdasd

功能增强(by jiuk2k):

如果将查找内容“[0-9][0-9][0-9]”改为“[0-9]*[0-9]”,对应1 或 123 或 12345 或 …

大家根据需要定制

相关内容还有很多,可以自己参考正则表达式的语法仔细研究一下

【3】正则表达式应用——删除每一行行尾的指定字符

因为这几个字符在行中也是出现的,所以肯定不能用简单的替换实现

比如

12345 1265345

2345

需要删除每行末尾的“345”

这个也算正则表达式的用法,其实仔细看正则表达式应该比较简单,不过既然有这个问题提出,说明对正则表达式还得有个认识过程,解决方法如下

在替换对话框中,启用“正则表达式”复选框

在查找内容里面输入“345$”

这里“$”表示从行尾匹配

如果从行首匹配,可以用“^”来实现,不过 EditPlus 有另一个功能可以很简单的删除行首的字符串

a. 选择要操作的行

b. 编辑-格式-删除行注释

c. 在弹出对话框里面输入要清除的行首字符,确定

【4】正则表达式应用——替换带有半角括号的多行

几百个网页中都有下面一段代码:

\n

在替换对话框启用“正则表达式”选项,这时就可以完成替换了

【5】正则表达式应用——删除空行

启动EditPlus,打开待处理的文本类型文件。

①、选择“查找”菜单的“替换”命令,弹出文本替换对话框。选中“正则表达式”复选框,表明我们要在查找、替换中使用正则表达式。然后,选中“替换范围”中的“当前文件”,表明对当前文件操作。

②、单击“查找内容”组合框右侧的按钮,出现下拉菜单。

③、下面的操作添加正则表达式,该表达式代表待查找的空行。(技巧提示:空行仅包括空格符、制表符、回车符,且必须以这三个符号之一作为一行的开头,并且以回车符结尾,查找空行的关键是构造代表空行的正则表达式)。

直接在”查找”中输入正则表达式“^[ \t]*\n”,注意\t前有空格符。

(1)选择“从行首开始匹配”,“查找内容”组合框中出现字符“^”,表示待查找字符串必须出现在文本中一行的行首。

(2)选择“字符在范围中”,那么在“^”后会增加一对括号“[]”,当前插入点在括号中。括号在正则表达式中表示,文本中的字符匹配括号中任意一个字符即符合查找条件。

(3)按一下空格键,添加空格符。空格符是空行的一个组成成分。

(4)选择“制表符”,添加代表制表符的“\t”。

(5)移动光标,将当前插入点移到“]”之后,然后选择“匹配 0 次或更多”,该操作会添加星号字符“*”。星号表示,其前面的括号“[]”内的空格符或制表符,在一行中出现0个或多个。

(6)选择“换行符”,插入“\n”,表示回车符。

④、“替换为”组合框保持空,表示删除查找到的内容。单击“替换”按钮逐个行删除空行,或单击“全部替换”按钮删除全部空行(注意:EditPlus有时存在“全部替换”不能一次性完全删除空行的问题,可能是程序BUG,需要多按几次按钮)。

java正则表达式通过java.util.regex包下的Pattern类与Matcher类实现(建议在阅读本文时,打开java API文档,当介绍到哪个方法时,查看java API中的方法说明,效果会更佳).

Pattern类用于创建一个正则表达式,也可以说创建一个匹配模式,它的构造方法是私有的,不可以直接创建,但可以通过Pattern.complie(String regex)简单工厂方法创建一个正则表达式,

Java代码示例:

Pattern p=Pattern.compile("\\w+");

p.pattern();//返回 \w+

­

pattern() 返回正则表达式的字符串形式,其实就是返回Pattern.complile(String regex)的regex参数

1.Pattern.split(CharSequence input)

Pattern有一个split(CharSequence input)方法,用于分隔字符串,并返回一个String[],我猜String.split(String regex)就是通过Pattern.split(CharSequence input)来实现的.

Pattern p=Pattern.compile("\\d+");

String[] str=p.split("我的QQ是:456456我的电话是:0532214我的邮箱是:[email protected]");

结果:str[0]="我的QQ是:" str[1]="我的电话是:" str[2]="我的邮箱是:[email protected]"

2.Pattern.matcher(String regex,CharSequence input)是一个静态方法,用于快速匹配字符串,该方法适合用于只匹配一次,且匹配全部字符串.

Pattern.matches("\\d+","2223");//返回true

Pattern.matches("\\d+","2223aa");//返回false,需要匹配到所有字符串才能返回true,这里aa不能匹配到

Pattern.matches("\\d+","22bb23");//返回false,需要匹配到所有字符串才能返回true,这里bb不能匹配到

3.Pattern.matcher(CharSequence input)

说了这么多,终于轮到Matcher类登场了,Pattern.matcher(CharSequence input)返回一个Matcher对象.

Matcher类的构造方法也是私有的,不能随意创建,只能通过Pattern.matcher(CharSequence input)方法得到该类的实例.

Pattern类只能做一些简单的匹配操作,要想得到更强更便捷的正则匹配操作,那就需要将Pattern与Matcher一起合作.Matcher类提供了对正则表达式的分组支持,以及对正则表达式的多次匹配支持.

Matcher m=p.matcher("22bb23");

m.pattern();//返回p 也就是返回该Matcher对象是由哪个Pattern对象的创建的

4.Matcher.matches() / Matcher.lookingAt() / Matcher.find()

Matcher类提供三个匹配操作方法,三个方法均返回boolean类型,当匹配到时返回true,没匹配到则返回false

matches()对整个字符串进行匹配,只有整个字符串都匹配了才返回true

m.matches();//返回false,因为bb不能被\d+匹配,导致整个字符串匹配未成功.

Matcher m2=p.matcher("2223");

m2.matches();//返回true,因为\d+匹配到了整个字符串

我们现在回头看一下Pattern.matcher(String regex,CharSequence input),它与下面这段代码等价

Pattern.compile(regex).matcher(input).matches()

lookingAt()对前面的字符串进行匹配,只有匹配到的字符串在最前面才返回true

m.lookingAt();//返回true,因为\d+匹配到了前面的22

Matcher m2=p.matcher("aa2223");

m2.lookingAt();//返回false,因为\d+不能匹配前面的aa

find()对字符串进行匹配,匹配到的字符串可以在任何位置.

m.find();//返回true

m2.find();//返回true

Matcher m3=p.matcher("aa2223bb");

m3.find();//返回true

Matcher m4=p.matcher("aabb");

m4.find();//返回false

5.Mathcer.start() / Matcher.end() / Matcher.group()

当使用matches(),lookingAt(),find()执行匹配操作后,就可以利用以上三个方法得到更详细的信息.

start()返回匹配到的子字符串在字符串中的索引位置.

end()返回匹配到的子字符串的最后一个字符在字符串中的索引位置.

group()返回匹配到的子字符串

Matcher m=p.matcher("aaa2223bb");

m.find();//匹配2223

m.start();//返回3

m.end();//返回7,返回的是2223后的索引号

m.group();//返回2223

Mathcer m2=m.matcher("2223bb");

m.lookingAt();  //匹配2223

m.start();  //返回0,由于lookingAt()只能匹配前面的字符串,所以当使用lookingAt()匹配时,start()方法总是返回0

m.end();  //返回4

m.group();  //返回2223

Matcher m3=m.matcher("2223bb");

m.matches();  //匹配整个字符串

m.start();  //返回0,原因相信大家也清楚了

m.end();  //返回6,原因相信大家也清楚了,因为matches()需要匹配所有字符串

m.group();  //返回2223bb

说了这么多,相信大家都明白了以上几个方法的使用,该说说正则表达式的分组在java中是怎么使用的.

start(),end(),group()均有一个重载方法它们是start(int i),end(int i),group(int i)专用于分组操作,Mathcer类还有一个groupCount()用于返回有多少组.

Pattern p=Pattern.compile("([a-z]+)(\\d+)");

m.find();  //匹配aaa2223

m.groupCount();  //返回2,因为有2组

m.start(1);  //返回0 返回第一组匹配到的子字符串在字符串中的索引号

m.start(2);  //返回3

m.end(1);  //返回3 返回第一组匹配到的子字符串的最后一个字符在字符串中的索引位置.

m.end(2);  //返回7

m.group(1);  //返回aaa,返回第一组匹配到的子字符串

m.group(2);  //返回2223,返回第二组匹配到的子字符串

现在我们使用一下稍微高级点的正则匹配操作,例如有一段文本,里面有很多数字,而且这些数字是分开的,我们现在要将文本中所有数字都取出来,利用java的正则操作是那么的简单.

Matcher m=p.matcher("我的QQ是:456456 我的电话是:0532214 我的邮箱是:[email protected]");

while(m.find()) {

    System.out.println(m.group());

}

输出:

456456

0532214

123

如将以上while()循环替换成

    System.out.print("start:"+m.start());

    System.out.println(" end:"+m.end());

则输出:

start:6 end:12

start:19 end:26

start:36 end:39

现在大家应该知道,每次执行匹配操作后start(),end(),group()三个方法的值都会改变,改变成匹配到的子字符串的信息,以及它们的重载方法,也会改变成相应的信息.

注意:只有当匹配操作成功,才可以使用start(),end(),group()三个方法,否则会抛出 java.lang.IllegalStateException,也就是当matches(),lookingAt(),find()其中任意一个方法返回true时,才可以使用.

6.Matcher.replaceAll(String replacement) / Matcher.replaceFirst(String replacement)

大家应该知道String.replaceAll()和String.replaceFirst()两个方法的功能,其实它与 Matcher.replaceAll()和Matcher.replaceFirst()的功能是一样的,只不过是使用方式不一样.例如我要将某文本中的所有数字变成*

使用String完成该要求

String str="我的QQ是:456456 我的电话是:0532214 我的邮箱是:[email protected]";

System.out.println(str.replaceAll("\\d","*"));

输出: 我的QQ是:****** 我的电话是:******* 我的邮箱是:aaa***@aaa.com

现在我们用Matcher完成该要求

Pattern p=Pattern.compile("\\d");

­Matcher m=p.matcher("我的QQ是:456456 我的电话是:0532214 我的邮箱是:[email protected]");

System.out.println(m.replaceAll("*"));

String.replaceAll()应该是调用了Matcher.replaceAll(),String.replaceAll()与下面这段代码等价

Pattern.compile(regex).matcher(str).replaceAll(replacement)

至于Matcher.replaceFirst()也很简单,它与String.replaceFirst()功能一样,我就不多说了.

str.replaceFirst(regex, replacement)与下面这段代码等价

Pattern.compile(regex).matcher(str).replaceFirst(replacement)

7.Matcher.appendReplacement(StringBuffer sb, String replacement) / Matcher.appendTail(StringBuffer sb)

­将当前匹配子串替换为指定字符串,并且将替换后的子串以及其之前到上次匹配子串之后的字符串段添加到一个StringBuffer对象里,而 appendTai­l(StringBuffer sb) 方法则将最后一次匹配工作后剩余的字符串添加到一个StringBuffer对象里.看例子:

StringBuffer sb=new StringBuffer();

m.find();  //匹配到456456

m.appendReplacement(sb,"*");  //将456456之前的字符串追加到sb,再将456456替换为*,并追加到sb

System.out.println(sb.toString());

m.appendTail(sb);  //将前面替换过的内容连接后面未替换过的内容,并放入sb

输出:

我的QQ是:*

我的QQ是:* 我的电话是:0532214 我的邮箱是:[email protected]

再看一个例子

    m.appendReplacement(sb,"*");

    System.out.println(sb.toString());

m.appendTail(sb);

System.out.println("使用appendTail()的最终内容是:"+sb.toString());

我的QQ是:* 我的电话是:*

我的QQ是:* 我的电话是:* 我的邮箱是:aaa*

使用appendTail()的最终内容是:我的QQ是:* 我的电话是:* 我的邮箱是:aaa*@aaa.com

关于这两个方法就介绍到这,如果不明白的话,还需要自己动下手,认真体会一下其内涵.

­8.Matcher.region(int start, int end) / Matcher.regionEnd() / Matcher.regionStart()

我们在做匹配操作时,默认去匹配的是整个字符串,例如有一字符串"aabbcc",使用"\\d+"去find()时,是从第一个a开始匹配, 也就是索引号为0的位置,开始去匹配,当索引号为0的位置没有匹配到时,就去下一个位置去匹配...直到匹配到子字符串或匹配完最后一个字符索引号才结束,很显然"\\d+"不能匹配"aabbcc",当它匹配完最后一个c时,结束本次匹配,宣告匹配失败,也就是说它会去匹配完整个字符串,能不能不去匹配完整个字符串呢,答案是可以的.

region(int start,int end)就是用来设置此匹配器的区域限制。

正在装载数据……

先来看一个例子.

String content="aaabb2233cc";

Matcher m=p.matcher(content);

System.out.println(m);

输出: java.util.regex.Matcher[pattern=\d+ region=0,11 lastmatch=]

可以看到region=0,11 表示start=0,end=11,更通俗的说就是当去匹配字符串,先从索引号为0的位置去匹配,如果匹配到了子字符串就返回,如果没有匹配到则到下一个位置去匹配,一直匹配到索引号为11-1的字符就结束匹配.

为什么是11呢,因为content.length()==11

现在你应该明白了它的作用,来看一个例子.

m.find();  //匹配到2223,返回true

Matcher m2=p.matcher(content);

m2.region(0,5);

m2.find();  //返回false,只去匹配索引号0至5-1的字符,没有匹配到

Matcher m3=p.matcher(content);

m2.region(3,8);

m2.find();  //返回true

m2.group();  //返回223,为什么,请数一下索引号就知道了.

Matcher.regionStart()返回region(int start,int end)中的start值,默认为0

Matcher.regionEnd()返回region(int start,int end)中的end值,默认为去匹配字符串的length()值

9.Matcher.reset() / Matcher.reset(CharSequence input)

用于重置匹配器。看示例

Pattern p=Pattern.compile("[a-z]+");

Matcher m=p.matcher(content);  //此时m刚创建出来,为最初状态

m.find();

m.group();  //返回aaabb

m.group();  //返回cc

Matcher m2=p.matcher(content);  //此时m2刚创建出来,为最初状态

m.reset();  //恢复到了最初状态,此时相当于m2刚创建出来

m.group();  //返回aaabb,相信大家应该知道了吧

Matcher.reset(CharSequence input) 恢复到最初状态,并将匹配字符串换成input,以后执行匹配操作时,就来匹配input,而不匹配原来的字符串了.

10.Matcher.toMatchResult()

大家查看一下java API 对Matcher类的说明,会发现它实现了MatchResult 接口,这个接口只有以下几个方法

groupCount()

group() / group(int i)

start() / start(int i)

end() / end(int i)

至于这几个方法的功能前面已经介绍过,现在我们来看一下toMatchResult() 是如何使用的

List list=new ArrayList();

    list.add(m.toMatchResult());

MatchResult matchResult=null;

Iterator it=list.iterator();

int i=1;

while(it.hasNext()) {

    matchResult=(MatchResult)it.next();

    System.out.print("第"+(i++)+"次匹配到的信息: ");

    System.out.println(matchResult.group()+"\t\t"+matchResult.start()+"\t"+matchResult.end());

第1次匹配到的信息: 456456           6        12

第2次匹配到的信息: 0532214        19      26

第3次匹配到的信息: 123                 36      39

现在你应该知道,toMatchResult()用于保存某次匹配后的信息,待以后再使用.

方法使用就说到这里,现在再介绍一个实例

有这样一个需求,有一个HTML文件,需要将其中的内容抽取出来,并不带HTML标签,如果使用正则表达式,这是一件很容易的事情. 前提是这个HTML文件只保留了<body></body>标签以内的内容.

String html="<div><font  color='red'>example1</font></div>"; //可以是任何html文件源代码,但格式一定要正确

Pattern p=Pattern.compile("<[^>]*>");

Matcher m=p.matcher(html);

String result=m.replaceAll("");

System.out.println(result);

输出:example1

参考资料:

java.util.regex的API文档

陈广佳的JAVA正则表达式--Pattern和Matcher