一、起源
这段代码的作用是将字符串中${param}替换为map中的数据
private static String replaceVariantOldVersion(String str, Map<String,String> variantMap){
Matcher m = Pattern.compile("\\$\\{.*?\\}").matcher(str);
StringBuffer rtn = new StringBuffer();
while(m.find()){
String foundStr = m.group();
String key = foundStr.substring("${".length(),foundStr.length()-1).trim();
String value = variantMap.get(key);
if(null == value){
value = "{UNKNOWN_VALUE}";
}
m.appendReplacement(rtn, value);
}
m.appendTail(rtn);
return rtn.toString();
}
例如
String str = "my name is ${name}";
Map<String,String> map = new HashMap<String,String>();
map.put("name", "lazy");
System.out.println(replaceVariantOldVersion(str,map));//输出my name is lazy
但如果是
String str = "my name is ${name}";
Map<String,String> map = new HashMap<String,String>();
map.put("name", "$.lazy");
System.out.println(replaceVariantOldVersion(str,map));
则会抛出异常
Exception in thread "main" java.lang.IllegalArgumentException: Illegal group reference
at java.util.regex.Matcher.appendReplacement(Unknown Source)
at lgc.tools.struts2.ConventionUtil.replaceVariantOldVersion(ConventionUtil.java:241)
at lgc.tools.struts2.ConventionUtil.main(ConventionUtil.java:269)
二、 分析
分析下appendReplacement源代码
public Matcher appendReplacement(StringBuffer sb, String replacement) {
// If no match, return error
if (first < 0)
throw new IllegalStateException("No match available");
// Process substitution string to replace group references with groups
int cursor = 0;
String s = replacement;
StringBuffer result = new StringBuffer();
while (cursor < replacement.length()) {
char nextChar = replacement.charAt(cursor);
if (nextChar == '\\') {
cursor++;
nextChar = replacement.charAt(cursor);
result.append(nextChar);
cursor++;
} else if (nextChar == '$') {
// Skip past $
cursor++;
// The first number is always a group
int refNum = (int)replacement.charAt(cursor) - '0';
if ((refNum < 0)||(refNum > 9))
throw new IllegalArgumentException(
"Illegal group reference");
cursor++;
// Capture the largest legal group string
boolean done = false;
while (!done) {
if (cursor >= replacement.length()) {
break;
}
int nextDigit = replacement.charAt(cursor) - '0';
if ((nextDigit < 0)||(nextDigit > 9)) { // not a number
break;
}
int newRefNum = (refNum * 10) + nextDigit;
if (groupCount() < newRefNum) {
done = true;
} else {
refNum = newRefNum;
cursor++;
}
}
// Append group
if (group(refNum) != null)
result.append(group(refNum));
} else {
result.append(nextChar);
cursor++;
}
}
// Append the intervening text
sb.append(getSubSequence(lastAppendPosition, first));
// Append the match substitution
sb.append(result.toString());
lastAppendPosition = last;
return this;
}
原来第二个参数replacement可以使用$n来引用分组!所以‘$’和‘\’都被当做特殊字符处理!replacement中的字符串$1代表匹配的分组1!
什么是分组?
看程序
Matcher m = Pattern.compile("((\\d\\d)(\\w))").matcher("11a22b");
StringBuffer sb = new StringBuffer();
while(m.find()){
m.appendReplacement(sb, "$0,$1,$2,$3;");
}
m.appendTail(sb);
System.out.println(sb);
输出的结果是11a,11a,11,a;22b,22b,22,b;
正则表达式((\d\d)(\w))有三个分组,每个括号包含的内容称作一个分组,并按照左括号出现的顺序给每个分组给予编号1,2,3,...,编号为0的分组代表整个被匹配的字符串。
例子程序中,
“11a”被 ((\d\d)(\w))匹配,$0是整个字符串,等于11a,此时sb是:11a
$1是(\d\d)(\w)撇配的内容,等于11a,此时sb是11a,11a
$2是(\d\d)匹配的内容,等于11,此时sb是11a,11a,11
$3是(\w)匹配的内容,等于a,此时sb是11a,11a,11,a;
相信大家到此应该大致明白分组的意义,如有问题请留言。
三、结论
回到正题,既然 appendReplacement(StringBuffer sb, String replacement)的replacement参数中'$'和'\',那么我就对这两个字符进行转义。
(其中'\'字符是被当做特殊字符处理是因为,java将\$当做普通的$进行处理,所以也'\'被当做了特殊字符。你可以尝试运行
String str = "my name is ${name}";
Map<String,String> map = new HashMap<String,String>();
map.put("name", "\\.lazy");
System.out.println(replaceVariantOldVersion(str,map));
同样会因为特殊字符而报错。
)
最终,我修正后的代码是
private static String replaceVariant(String str, Map<String,String> variantMap){
Matcher m = Pattern.compile("\\$\\{.*?\\}").matcher(str);
StringBuffer rtn = new StringBuffer();
while(m.find()){
String foundStr = m.group();
String key = foundStr.substring("${".length(),foundStr.length()-1).trim();
String value = variantMap.get(key);
if(null == value){
value = "{UNKNOWN_VALUE}";
}
String valueReplacement = value.replaceAll("\\\\","\\\\\\\\").replaceAll("\\$", "\\\\\\$");
m.appendReplacement(rtn, valueReplacement);
}
m.appendTail(rtn);
return rtn.toString();
}
我们重点关注
String valueReplacement = value.replaceAll("\\\\","\\\\\\\\").replaceAll("\\$", "\\\\\\$");
其实就是将一个 \ 变成 \\ ,一个 $ 变成 \$
可能会有人问,replaceAll的第一个参数是正则表达式,所以需要4个\,为什么第二个参数也需要那么多个\?这也是我开始写代码是遇到的疑惑,实际上,replaceAll最终也是调用我们刚分析过的函数
public Matcher appendReplacement(StringBuffer sb, String replacement)
replaceAll的第二个参数将会传到appendReplacement的replacement,所以
replaceAll的第二个参数中的\和$也属于特殊字符串!
这也解释了,我们将文件路径分隔符/替换为\的时候,为什么需要那么多\\\\了。
String s = "E:/mydir/mydir2/mdir3";
//System.out.println(s.replaceAll("/","\\"));报错
System.out.println(s.replaceAll("/","\\\\"));//正确
使用replaceAll的时候,我们需要记住的是,不论是第一个参数还是第二参数,4个\才代表自然字符串中的一个\。在这里我也期待java能推出自然字符串的语法,像python的r"\n"一样。
分享到:
相关推荐
ReplaceAll 多文件文本批量替换工具 依赖.net framework2.0
replace和replaceAll是JAVA中常用的替换字符的方法,它们的区别是: 1)replace的参数是char和CharSequence,即可以支持字符的替换,也支持字符串的替换(CharSequence即字符串序列的意思,说白了也是字符串); 2)...
本文是对JS中实现replaceAll的方法进行了详细的总结介绍,需要的朋友可以过来参考下,希望对大家有所帮助
相信会java的同学估计都用过replace、replaceAll、replaceFirst这三个函数,可是,我们真的懂他们吗?下面通过这篇文章大家再来好好学习学习下这几个函数。
JavaScript 中使用 replace 达到 replaceAll的效果,其实就用利用的正则的全局替换。
JS 没有提供replaceAll这样的方法。使用正则表可以达成Replace 的效果,感兴趣的朋友看看下面的示例
Console.WriteLine(ms.ReplaceAll("hello world","l","*")); //输出”he**o wor*d" ArrayList list=ms.GetPosList("hello world","l"); foreach(int pos in list){ Console.WriteLine("" + pos); } //输出2,3,9
主要介绍了java中replace()和replaceAll()的区别,两者都是常用的替换字符的方法,感兴趣的小伙伴们可以参考一下
string.prototype.replaceall 用于String.prototype.replaceAll的ES Proposal规范填充程序。 如果不可用或不String.prototype.replaceAll调用其“ shim”方法对String.prototype.replaceAll进行填充。 该软件包...
将一段文字中的字符串全部替换 PowerBuilder ReplaceAll
数据结构 二叉树 替换子树replaceAll \***********************************************************************************************/
var replaceall = require("replaceall"); 使用替换 var result = replaceall ( "instances of this" , "with this string" , "in this string" ) ; 例子 var original = "hello world goodbye world" ; ...
replaceAll()方法的作用和replaceWith()方法是完全一样的。 语法结构: 代码如下:$(content).replaceAll(selector) 参数列表: 参数 描述 content 用于替换的内容。 selector 用于查找所要被替换的元素 ...
htmlStr =htmlStr.replaceAll("∀", "∀"); htmlStr =htmlStr.replaceAll("∂", "∂"); htmlStr =htmlStr.replaceAll("&exists;", "∃"); htmlStr =htmlStr.replaceAll("∅", "∅"); htmlStr...
主要给大家介绍了关于Java replaceAll()方法报错Illegal group reference的解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
主要介绍了Java中replace与replaceAll区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
介绍了js replace 与replaceall实例用法详解,有需要的朋友可以参考一下
主要给大家介绍了关于String.replaceAll方法,正则妙用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧