String类replaceAll方法正则替换深入分析
作者网址:
https://my.oschina.net/shipley/blog/98973
背景:
前几天有人发了一个关于下面问题的贴,对这个有点好奇,故花时间做了点研究。
对单个反斜杠字符串替换成双斜杠的Java实现如下:
String s = "\\";
方法一:String sr1 = s.replaceAll("\\\\", "\\\\\\\\");
方法二:String sr1 = s.replaceAll("\\\\", "$0$0");
我第一眼看到比较困惑,下面慢慢来分析。
分析:
对String类的replaceAll(String reg, String replacement)方法分析
一、两点疑惑
A. 为啥第一个参数reg必须是”\\\\”?
B. 为啥第二个参数replacement 必须是”\\\\\\\\”?
二、解答
A.因为reg这个参数表示一个正则表达式,首先字符串“\\\\”被转义后代表的实际是字符串\\,这就是正则表达式,那么在正则表达式里也有转义,那么这个正则匹配的就是\
B.首先字符串“\\\\\\\\”被转义后实际代表的其实是字符串\\\\;
接下来才是重点:
查看源码replaceAll方法的实现如下
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
其中Pattern.compile(regex).matcher(this), 返回的是一个Matcher对象。
先简单介绍java.util.regex.Matcher类,是通过解释 Pattern 对 字符序列执行匹配操作的引擎,其中持有对当前Pattern对象和当前String对象的引用。
执行一次调用其find方法,即对字符串执行一次从左向右的以Pattern为正则的匹配,并记录下匹配结果字符串的开始和结束位置索引,以及更新一个记录当前匹配结果的分组groups。
顺藤摸瓜,进入Matcher类的replaceAll方法,继续查看源码,
public String replaceAll(String replacement) {
// 对当前Matcher类进行重置,即对其中记录匹配结果的开始和结束位置索引,以及分组信息重置
reset();
// 执行第一次搜索
boolean result = find();
if (result) {
// 第一次搜索匹配成功
// 用于记录最终的替换结果字符串
StringBuffer sb = new StringBuffer();
// 循环搜索
do {
// *重点在此方法内:用于将从上一次匹配子字符串的下一个索引位置开始,到当前匹配的子字符串的结束索引位置的所有字符 append到字符串sb中
// 有点绕,可以暂时跳过,下面会对该方法进一步分析
appendReplacement(sb, replacement);
result = find();
} while (result);
// 将从最后一次匹配子字符串的下一个索引位置,到字符串的结尾的所有字符append到字符串sb中
appendTail(sb);
return sb.toString();
}
return text.toString();
}
继续南下,进入Matcher类的appendReplacement方法,
public Matcher appendReplacement(StringBuffer sb, String replacement) {
...省略部分代码
// 用于跟踪replacement字符串的索引
int cursor = 0;
String s = replacement;// Java api源码也有垃圾代码啊,呵呵 (s局部变量并未在后续代码中被使用)
// 对当前匹配到子字符串替换后的结果字符串
StringBuffer result = new StringBuffer();
// 遍历replacement字符串
while (cursor < replacement.length()) {
char nextChar = replacement.charAt(cursor);
if (nextChar == '\\') {
// 重点1:当字符为\时,跳过,并获取其后面的字符,追加到result
cursor++;
nextChar = replacement.charAt(cursor);
result.append(nextChar);
cursor++;
} else if (nextChar == '$') {
// 重点2:当字符为$时,跳过,并获取其后面的数值,并以此如果$后面第一个不为数字则抛异常,
// Skip past $
cursor++;
// The first number is always a group
int refNum = (int)replacement.charAt(cursor) - '0';
// 此处代码用于计算$符号后的数值,数值结果赋予refNum
...省略部分代码
// group(refNum) 用于获取正则表达式第refNum个分组表示的字符串,不详说了
if (group(refNum) != null)
result.append(group(refNum)); // 追加到result
} else {
// 当前字符不为\ 或 $ 则直接追加到result
result.append(nextChar);
cursor++;
}
}
// 将从上一次匹配的子字符串的结尾索引,到当前匹配的第一个字符串索引的字符串追加到sb
// lastAppendPosition参数为上一次执行appendReplacement方法最后追加的字符在原始字符串中的索引位置。
// first 参数为当前待替换的子字符串的首个字符在原始字符串中的索引位置
sb.append(getSubSequence(lastAppendPosition, first));
// 将当前配置子字符串替换后的结果字符串追加到sb
sb.append(result.toString());
// 更新lastAppendPosition,供下一个匹配执行appendReplacement方法使用
lastAppendPosition = last;
/*
到此, sb中追加了当前匹配的子字符串与前一次匹配子字符串中间的字符,以及当前匹配子字符串被替换后的字符串
*/
return this;
}
分析结束。
总结
1、replaceAll中第二个参数replacement中,\有转义的作用,$用于获取分组匹配的当前子字符串
现在想想为什么要引入这个\转义的功能? 我的猜测是 ----- 因为引入了$符的分组功能,所以为了解决能输出$字符,故引入\转义功能
2、有助于理解Java的正则表达式;
3、世界上没有十全十美的代码,Java源码里也有垃圾代码,呵呵。
提供几个问题大家可以实践下:
1、对两个反斜杠字符串每个字符串都替换成双斜杠,如何实现?
即String s = "\\\\"; 替换成 String sr = "\\\\\\";
2、将单反斜杠替换成美元符,如何实现?
即String s = "\\"; 替换成 String sr = "$";
3、String s = "Jack is Rose's boyfriends."
使用$分组替换功能 替换成
String sr = "Rose is Jack's girlfriends."
String类replaceAll方法正则替换深入分析的更多相关文章
- 《java入门第一季》之类(String类常见方法小叙)
String类下面的构造方法和一些常见的方法: /* * 字符串:就是由多个字符组成的一串数据.也可以看成是一个字符数组. * 通过查看API,可以知道 * A:字符串字面值"abc&quo ...
- C++中string类的方法
C++ string类的方法 具体每个方法怎么使用,可以参考相应的链接. 总的链接为http://www.cplusplus.com/reference/string/string/(C++参考文档) ...
- string类find_first_not_of ()方法
string类find_first_not_of ()方法 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://xfqxj.blog. ...
- 深入分析Java的String类的方法与特点
字符串是任何编程语言都必须支持的变量类型,有些编程语言是直接提供了原生的变量类型,有些编程语言则使用语法特性以 SDK 的形式提供支持.在Java编程平台中,对字符串的支持使用了后者的形式,就是通过在 ...
- Java中String类的方法及说明
String : 字符串类型 一. String sc_sub = new String(c,3,2); // String sb_copy = new String(sb) ...
- String的replaceAll方法中的正则表达式用法
项目里面 需要对已手机号码进行 如下的显示 比如15088688388 要显示为150****8388的效果 实现这个简单的效果 方法有很多 我想试试用正则表达式去实现 查了点资料最终试出来以下方法可 ...
- 【转载】Java中String类的方法及说明
转载自:http://www.cnblogs.com/YSO1983/archive/2009/12/07/1618564.html String : 字符串类型 一. String sc_ ...
- 『Java』String类使用方法
Java中的字符串 java.lang.String类表示字符串类,Java程序中所有字符串文字都可以看作实现该类的实例. 特点: 字符串不可变:字符串的值在创建后不能在发生改变 public cla ...
- JDK6与JDK7中String类subString()方法的区别
1.subString()方法的作用 subString(int beginIndex, int endIndex)方法的返回的是以beginIndex开始到 endIndex-1结束的某个调用字符串 ...
随机推荐
- 【项目】UICollectionViewFlowlayout再一次自定义
项目中好友列表需要使用UICollection完成,加入了长按点击颤抖删除按钮
- python 培训之爬虫
1. 输入文件为 fufang_list.txt yaofang_a aaiwan 阿艾丸 yaofang_a aaiwulingsan 阿艾五苓散 yaofang_a acaitang 阿菜汤 ya ...
- 数组Arrays
1.toString 方法 Arrays的toString方法可以方便的输出一个数组的字符串形式,方便查看,它有九个重载的方法,包括八种基本类型数组和一个对象类型数组,这里列举两个: public s ...
- maven学习讲解
参考链接:http://www.cnblogs.com/bigtall/archive/2011/03/23/1993253.html 1.前言 Maven,发音是[`meivin],"专家 ...
- PHP中“简单工厂模式”实例讲解
原创文章,转载请注明出处:http://www.cnblogs.com/hongfei/archive/2012/07/07/2580776.html 简单工厂模式:①抽象基类:类中定义抽象一些方法, ...
- 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【七】——实现资源的分页
系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 这篇文章我们将使用不同的方式实现手动分页(关于高端大气上档次的OData本文暂不涉及,但有可 ...
- array_filter移除空数组
$arr = array( "0"=> "564645", "1"=>"", "2"=& ...
- OC-成员变量的作用域
#import <Foundation/Foundation.h> @interface Person : NSObject { int _no; @public // 在任何地方都能直接 ...
- CXF bus interceptor配置
作用:BUS是cxf的支架,它主要担当扩展及拦截器提供者的角色. 在这里主要讲讲 bus的interceptor的功能 目前配置cxf的interceptor主要有2中方法: 1.通过xml配置文件的 ...
- SQLSERVER2008R2正确使用索引
T1表 10000000万条数据,(插入时间36分钟,count(*)查询19秒,空间占用670M左右) 1.真正充分的利用索引比如like '张%' 就是符合SARG(符合扫描参数)标准而like ...