String.replaceAll方法以及java.util.regex.Matcher类的妙用

问题由来

今儿不小心在群里看到一位朋友发了下面的代码: title 通过输出可以看到,这段代码的作用是把驼峰命名格式的字符串替换成下划线分割,这个功能比较简单,但是吸引我的却是他的代码。

"createTime".replaceAll("([A-Z]+)","_$1")

这行代码其实很简单,就是调用了String类的replaceAll方法,方法的第一个参数是正则表达式,第二个参数是将要被替换成的新值。让我惊奇的是他代码中,replaceAll的第二个参数,也就是JDK文档中名为replacement的参数,竟然是"_$1",这是什么鬼?还支持类似占位符这样的东西?我一直都不知道。。

问题探索

由于之前研究过一段正则表达式,通过观察replaceAll的第一个参数([A-Z]+),我猜想,这个应该是用到了正则表达式的分组,对应JDK中,就是java.util.regex.Matcher类的group()方法。 于是看了下String.replaceAll方法是如何实现的。 JDK源码如下:

public String replaceAll(String regex, String replacement) {
        return Pattern.compile(regex).matcher(this).replaceAll(replacement);
    }

哦,原来它底层就是用了Matcher,只不过用的是Matcher自己的replaceAll方法。 去看它的文档: title

原来这个方法的参数果然有鬼,看下实现代码:

public String replaceAll(String replacement) {
        reset();
        boolean result = find();
        if (result) {
            StringBuffer sb = new StringBuffer();
            do {
                appendReplacement(sb, replacement);
                result = find();
            } while (result);
            appendTail(sb);
            return sb.toString();
        }
        return text.toString();
    }

里面关键的部分就是文档中说的appendReplacement方法,这个方法的文档: title

看到这里明白了,原来这个方法的replacement参数可以通过$字符来指代Matcher通过正则匹配得到的分组,支持${name}和$number 两种方式,这里对应的就是Matcher类的group(name)和group(int)两个方法。

结论

  1. String的replaceAll方法实际上是通过java.util.regex.Matcher类的replaceAll()方法实现的
  2. java.util.regex.Matcher类replaceAll方法又是通过调用appendReplacement方法实现替换逻辑
  3. Matcher类的appendReplacement方法的replacement参数支持通过$符号来指代Matcher匹配的分组

关于Matcher类的分组可以参考下面的示例代码

@Test
    public void testMatcherGroup() {
        String data = "哈哈哈,我的手机号码是:12345678901,你会打给我吗";
        //通过Matcher的分组功能,可以提取出上面字符串中的手机号
        Matcher matcher = Pattern.compile(".*(我的手机号码是:([0-9]{11}))").matcher(data);
        while (matcher.find()) {
            System.out.println(matcher.group(0));
            System.out.println(matcher.group(1));
            System.out.println(matcher.group(2));
        }
    }

输出结果:

哈哈哈,我的手机号码是:12345678901
我的手机号码是:12345678901
12345678901

关于group: group(0)表示整个字符串 group(1)表示第一个匹配的,上面的例子中就是(我的手机号码是:([0-9]{11}))部分 group(2)表示第二个匹配的,上面的例子中就是([0-9]{11})部分

使用分组可以用来提取字符串中的目标字符串值,很好用!

资源

驼峰命名转下划线命名

 public static String camelToUnderline(String camelName) {
        return camelName.replaceAll("([A-Z]+)", "_$1").toLowerCase();
    }

下划线命名转驼峰

这个稍微麻烦点,是模仿者Matcher.replaceAll方法写的。

public static String underlineToCamel(String underlineName) {
        Matcher matcher = Pattern.compile("(_[a-z]{1})").matcher(underlineName);
        StringBuffer result = new StringBuffer();
        while (matcher.find()) {
            String replacement = matcher.group(1);
            matcher.appendReplacement(result, replacement.replace("_", "").toUpperCase());
        }
        matcher.appendTail(result);
        return result.toString();
    }

另外,Mybatis Generator插件源码中的也提供了类似方法(JavaBeansUtil.getCamelCaseString),这里做了下简单修改

public static String getCamelCaseString(String inputString) {
        StringBuilder sb = new StringBuilder();

        boolean nextUpperCase = false;
        for (int i = 0; i < inputString.length(); i++) {
            char c = inputString.charAt(i);

            switch (c) {
                case '_':
                case '-':
                case '@':
                case '$':
                case '#':
                case ' ':
                case '/':
                case '&':
                    if (sb.length() > 0) {
                        nextUpperCase = true;
                    }
                    break;

                default:
                    if (nextUpperCase) {
                        sb.append(Character.toUpperCase(c));
                        nextUpperCase = false;
                    } else {
                        sb.append(Character.toLowerCase(c));
                    }
                    break;
            }
        }
        
        return sb.toString();
    }

转载于:https://my.oschina.net/newever/blog/735467

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值