我想要替换这两个示例中"name“节点的值。我使用regex group来匹配并替换它。分组工作正常,但替换不起作用。
input 1
<xml
<user:address>.../</user:address>
<user:name>foo</user:name>
</xml>
input 2
<xml
<user:address>.../</user:address>
<street:name>bar</street:name>
</xml>
private static final String NAME_GROUP = "name";
public static final Pattern pattern = Pattern.compile("<.*:name>" + "(?<" + NAME + ">.*)</.*:name>");
final Matcher nameMatcher = pattern.matcher(str);
final String s = nameMatcher.find() ? nameMatcher.group(NAME_GROUP) : null;
System.out.println(s);
//foo
//bar
现在,当我替换掉
String output = nameMatcher.replaceFirst("hello")
I get
hello</xml>
虽然我对以下内容有所期待
<xml
<user:address>.../</user:address>
<user:name>hello</user:name>
</xml>
对于这两个示例。为什么组在工作,而不是替换?
发布于 2019-05-23 17:28:21
String
和Matcher
中的replaceFirst
/replaceAll
操作将始终替换整个匹配项。它们可以归结为如下的实现
public static String replace(
CharSequence source, Pattern p, String replacement, boolean all) {
Matcher m = p.matcher(source);
if(!m.find()) return source.toString();
StringBuffer sb = new StringBuffer();
do m.appendReplacement(sb, replacement); while(all && m.find());
return m.appendTail(sb).toString();
}
注意,在Java9之前,我们必须在这里使用StringBuffer
而不是StringBuilder
。
当我们忽略在替换字符串中具有组引用的功能时,我们可以向下钻取更深一层的逻辑,并获得
public static String replaceLiteral(
CharSequence source, Pattern p, String replacement, boolean all) {
Matcher m = p.matcher(source);
if(!m.find()) return source.toString();
StringBuilder sb = new StringBuilder();
int lastEnd = 0;
do {
sb.append(source, lastEnd, m.start()).append(replacement);
lastEnd = m.end();
} while(all && m.find());
return sb.append(source, lastEnd, source.length()).toString();
}
对于这段代码,很容易更改逻辑来替换特定的命名组,而不是整个匹配:
public static String replaceGroupWithLiteral(
CharSequence source, Pattern p, String groupName, String replacement, boolean all) {
Matcher m = p.matcher(source);
if(!m.find()) return source.toString();
StringBuilder sb = new StringBuilder();
int lastEnd = 0;
do {
sb.append(source, lastEnd, m.start(groupName)).append(replacement);
lastEnd = m.end(groupName);
} while(all && m.find());
return sb.append(source, lastEnd, source.length()).toString();
}
这已经足够实现您的示例了:
private static final String NAME_GROUP = "name";
public static final Pattern pattern
= Pattern.compile("<.*:name>" + "(?<" + NAME_GROUP + ">.*)</.*:name>");
String input =
"<xml\n"
+ " <user:address>.../</user:address>\n"
+ " <user:name>foo</user:name>\n"
+ "</xml>\n";
String s = replaceGroupWithLiteral(input, pattern, NAME_GROUP, "hello", false);
System.out.println(s);
<xml
<user:address>.../</user:address>
<user:name>hello</user:name>
</xml>
不过,我可能会用像这样的东西
public static final Pattern pattern
= Pattern.compile("<([^<>:]*?:name)>" + "(?<" + NAME_GROUP + ">.*)</\\1>");
如上所述(通过方法名称可以清楚地看出),这与普通的regex替换操作不同,因为它总是按字面意思插入替换。要获得与原型相同的行为,需要更复杂和效率更低的代码,所以我只在确实需要引用组时才使用它(或者语法被认为是合同中的替代语法)。
public static String replaceGroup(
CharSequence source, Pattern p, String groupName, String replacement, boolean all) {
Matcher m = p.matcher(source);
if(!m.find()) return source.toString();
StringBuffer sb = new StringBuffer();
do {
int s = m.start(), gs = m.start(groupName), e = m.end(), ge = m.end(groupName);
String prefix = s == gs? "":
Matcher.quoteReplacement(source.subSequence(s, gs).toString());
String suffix = e == ge? "":
Matcher.quoteReplacement(source.subSequence(ge, e).toString());
m.appendReplacement(sb, prefix+replacement+suffix);
} while(all && m.find());
return m.appendTail(sb).toString();
}
有了这个,如果我们使用,例如
String s = replaceGroup(input, pattern, NAME_GROUP, "[[${"+NAME_GROUP+"}]]", false);
我们会得到
<xml
<user:address>.../</user:address>
<user:name>[[foo]]</user:name>
</xml>
https://stackoverflow.com/questions/56228346
复制相似问题