Medusar's Blog
敬畏知识,谦逊前行
Toggle navigation
Medusar's Blog
主页
Booklist
Resources
About Me
归档
标签
String.replaceAll方法以及java.util.regex.Matcher类的妙用
java基础
2016-06-21 20:51:29
522
0
0
medusar
java基础
## 问题由来 今儿不小心在群里看到一位朋友发了下面的代码:  通过输出可以看到,这段代码的作用是把驼峰命名格式的字符串替换成下划线分割,这个功能比较简单,但是吸引我的却是他的代码。 ``` java "createTime".replaceAll("([A-Z]+)","_$1") ``` 这行代码其实很简单,就是调用了String类的replaceAll方法,方法的第一个参数是正则表达式,第二个参数是将要被替换成的新值。让我惊奇的是他代码中,replaceAll的第二个参数,也就是JDK文档中名为replacement的参数,竟然是"_$1",这是什么鬼?还支持类似占位符这样的东西?我一直都不知道。。 ## 问题探索 由于之前研究过一段正则表达式,通过观察replaceAll的第一个参数`([A-Z]+)`,我猜想,这个应该是用到了正则表达式的分组,对应JDK中,就是`java.util.regex.Matcher`类的`group()`方法。 于是看了下String.replaceAll方法是如何实现的。 JDK源码如下: ``` java public String replaceAll(String regex, String replacement) { return Pattern.compile(regex).matcher(this).replaceAll(replacement); } ``` 哦,原来它底层就是用了Matcher,只不过用的是Matcher自己的replaceAll方法。 去看它的文档:  原来这个方法的参数果然有鬼,看下实现代码: ``` java 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方法`,这个方法的文档:  看到这里明白了,原来这个方法的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类的分组可以参考下面的示例代码 ``` java @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)); } } ``` 输出结果: ``` bash 哈哈哈,我的手机号码是:12345678901 我的手机号码是:12345678901 12345678901 ``` 关于group: group(0)表示整个字符串 group(1)表示第一个匹配的,上面的例子中就是`(我的手机号码是:([0-9]{11}))`部分 group(2)表示第二个匹配的,上面的例子中就是`([0-9]{11})`部分 使用分组可以用来提取字符串中的目标字符串值,很好用! ## 资源 ### 驼峰命名转下划线命名 ``` java public static String camelToUnderline(String camelName) { return camelName.replaceAll("([A-Z]+)", "_$1").toLowerCase(); } ``` ### 下划线命名转驼峰 这个稍微麻烦点,是模仿者Matcher.replaceAll方法写的。 ``` java 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`),这里做了下简单修改 ``` java 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(); } ```
上一篇:
InfluxDB入门
下一篇:
Redis事务
0
赞
522 人读过
新浪微博
微信
腾讯微博
QQ空间
人人网
Please enable JavaScript to view the
comments powered by Disqus.
comments powered by
Disqus
文档导航