文档章节

String类replaceAll方法正则替换深入分析

囚兔
 囚兔
发布于 2012/12/28 21:24
字数 1335
阅读 20165
收藏 11

背景:  
    前几天有人发了一个关于下面问题的贴,对这个有点好奇,故花时间做了点研究。
    
    对单个反斜杠字符串替换成双斜杠的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."
 

© 著作权归作者所有

共有 人打赏支持
囚兔

囚兔

粉丝 39
博文 86
码字总数 47164
作品 1
南京
程序员
私信 提问
加载中

评论(1)

古月胡三水涛
好文章,都没人评论
【JAVA】找出文本中指定规律的字符,并替换其中某些字符

这代码有什么用? 我能帮您从一个文本文件中找出所有 指定规律的字符,并且还能替换结果中的某个指定字符,最后再打印出来。 这里的“规律”是指 匹配正则表达式。 怎么实现的? 文本文件的读...

周田
2015/08/21
0
0
【计算机本科补全计划】Java学习笔记(十) Java 正则表达式

正文之前 最近变故颇多,道心已破,诸位道友多多见谅。正在重铸道基,以期大道可成,从此逍遥万代。故Java之事,后续时断时续,敬请见谅! (郑重声明:本文非原创,只是笔记,取自菜鸟编程!...

HustWolf
2017/12/10
0
0
Java 反斜杠如何转义的问题

引用 说明:有正斜杠与反斜杠之分,正斜杠,一般就叫做斜杠,符号为“/”;反斜杠的符号为“” 引用 斜杠(/)在JAVA中没有什么特别的意义,就是代表一个字符‘/’; 反斜杠()则不然,它和紧...

Airship
2016/03/01
79
0
关于java字符串常用一些api 效率比拼小结(java对大型的字符串api处理效率比拼)

比如说我们要处理几十万个的字符串的处理,可能比如字符串的正则替换,比如replace、replaceAll,字符串的拼接或添加使用 +,StringBuilder的append,字符串的分割使用split。然而往往如果我...

DannyCoder
2018/09/05
0
0
java replaceAll() 方法要用 4 个反斜杠,表示一个反斜杠,该怎么解决

java replaceAll() 方法要用 4 个反斜杠,表示一个反斜杠 java replaceAll() 方法要用 4 个反斜杠,表示一个反斜杠 例如 str1="aabbb"; str2="aa'bbb"; 要想替换成 str1="aa\bbb";str2="aa'bbb......

Airship
2016/04/18
56
0

没有更多内容

加载失败,请刷新页面

加载更多

移植Modbus到STM32F103(2):移植FreeModbus到usart3并运行示例代码

FreeModbus是Modbus的一个被广泛移植的实现。其源码在github,最新版是1.6。 FreeModbus支持Modbus功能码里的0x01~0x06,0x0F~0x11和0x17,对一些功能比如异常诊断和读事件计数等功能码并没有...

Konstantine
47分钟前
1
0
浅谈神经网络(神经网络篇)

背景 之前写过浅谈神经网络基础篇,简单介绍下机器学习这块内容,用于扫盲。本文正式将神经网络,这部分是深度学习的基础。了解完可以掌握强大的机器学习的方法,也可以更好的了解深度学习。...

Uknowzheng
今天
3
0
移动硬盘变为RAW格式后的修复

在Mac上使用自己的移动硬盘结果文件系统格式变为RAW; 在自己windows笔记本上使用chkdsk H: /F进行修复,修复日志如下: C:\Users\mengzhang6>chkdsk H: /F文件系统的类型是 NTFS。卷标是 do...

晨猫
今天
3
0
10 Git —— 标签管理

10 Git —— 标签管理 本节内容: 命令git tag <tagname>用于新建一个标签,默认为HEAD,也可以指定一个commit id;命令git tag -a <tagname> -m "blablabla..."可以指定标签信息;命令git......

lwenhao
今天
3
0
小程序设置垂直居中,水平居中

如果子容器中的view需要居中的话,那需要在父容器中设置居中 水平居中: display: flex; flex-direction: column; align-items: center; 垂直居中 display: flex;align-items: cen...

淘幻幻
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部