我有这段代码,我想知道,如果我只能替换 Java 正则表达式中的组(不是所有模式)。代码:
//...
Pattern p = Pattern.compile("(\\d).*(\\d)");
String input = "6 example input 4";
Matcher m = p.matcher(input);
if (m.find()) {
//Now I want replace group one ( (\\d) ) with number
//and group two (too (\\d) ) with 1, but I don't know how.
}
使用 $n
(其中 n 是一个数字)来引用 replaceFirst(...)
中捕获的子序列。我假设您想用文字字符串 "number" 替换第一组,用第一组的值替换第二组。
Pattern p = Pattern.compile("(\\d)(.*)(\\d)");
String input = "6 example input 4";
Matcher m = p.matcher(input);
if (m.find()) {
// replace first number with "number" and second number with the first
String output = m.replaceFirst("number $3$1"); // number 46
}
考虑将 (\D+)
用于第二组而不是 (.*)
。 *
是一个贪心匹配器,首先会消耗最后一个数字。当匹配器意识到最后的 (\d)
没有可匹配的内容时,它必须回溯,然后才能匹配到最后一个数字。
您可以使用 Matcher#start(group)
和 Matcher#end(group)
构建通用替换方法:
public static String replaceGroup(String regex, String source, int groupToReplace, String replacement) {
return replaceGroup(regex, source, groupToReplace, 1, replacement);
}
public static String replaceGroup(String regex, String source, int groupToReplace, int groupOccurrence, String replacement) {
Matcher m = Pattern.compile(regex).matcher(source);
for (int i = 0; i < groupOccurrence; i++)
if (!m.find()) return source; // pattern not met, may also throw an exception here
return new StringBuilder(source).replace(m.start(groupToReplace), m.end(groupToReplace), replacement).toString();
}
public static void main(String[] args) {
// replace with "%" what was matched by group 1
// input: aaa123ccc
// output: %123ccc
System.out.println(replaceGroup("([a-z]+)([0-9]+)([a-z]+)", "aaa123ccc", 1, "%"));
// replace with "!!!" what was matched the 4th time by the group 2
// input: a1b2c3d4e5
// output: a1b2c3d!!!e5
System.out.println(replaceGroup("([a-z])(\\d)", "a1b2c3d4e5", 2, 4, "!!!"));
}
检查 online demo here。
很抱歉打死马,但没有人指出这一点有点奇怪 - “是的,你可以,但这与你在现实生活中使用捕获组的方式相反”。
如果您按照预期的方式使用 Regex,则解决方案就像这样简单:
"6 example input 4".replaceAll("(?:\\d)(.*)(?:\\d)", "number$11");
或者正如下面的 shmosel 正确指出的那样,
"6 example input 4".replaceAll("\d(.*)\d", "number$11");
...因为在您的正则表达式中根本没有充分的理由对小数进行分组。
您通常不会在要丢弃的字符串部分上使用捕获组,而是在要保留的字符串部分上使用它们。
如果你真的想要你想要替换的组,你可能想要的是一个模板引擎(例如,moustache、ejs、StringTemplate,...)。
顺便说一句,即使是正则表达式中的非捕获组也只是在正则表达式引擎需要它们识别和跳过可变文本的情况下存在。例如,在
(?:abc)*(capture me)(?:bcd)*
如果您的输入看起来像“abcabccapture mebcdbcd”或“abccapture mebcd”,甚至只是“捕获我”,您就需要它们。
或者反过来说:如果文本始终相同,而您没有捕捉到它,则根本没有理由使用组。
\d(.*)\d
就足够了。
$11
。为什么是 11?
您可以使用 matcher.start() 和 matcher.end() 方法来获取组位置。因此,使用此位置,您可以轻松替换任何文本。
替换输入中的密码字段:
{"_csrf":["9d90c85f-ac73-4b15-ad08-ebaa3fa4a005"],"originPassword":["uaas"],"newPassword":["uaas"],"confirmPassword":["uaas"]}
private static final Pattern PATTERN = Pattern.compile(".*?password.*?\":\\[\"(.*?)\"\\](,\"|}$)", Pattern.CASE_INSENSITIVE);
private static String replacePassword(String input, String replacement) {
Matcher m = PATTERN.matcher(input);
StringBuffer sb = new StringBuffer();
while (m.find()) {
Matcher m2 = PATTERN.matcher(m.group(0));
if (m2.find()) {
StringBuilder stringBuilder = new StringBuilder(m2.group(0));
String result = stringBuilder.replace(m2.start(1), m2.end(1), replacement).toString();
m.appendReplacement(sb, result);
}
}
m.appendTail(sb);
return sb.toString();
}
@Test
public void test1() {
String input = "{\"_csrf\":[\"9d90c85f-ac73-4b15-ad08-ebaa3fa4a005\"],\"originPassword\":[\"123\"],\"newPassword\":[\"456\"],\"confirmPassword\":[\"456\"]}";
String expected = "{\"_csrf\":[\"9d90c85f-ac73-4b15-ad08-ebaa3fa4a005\"],\"originPassword\":[\"**\"],\"newPassword\":[\"**\"],\"confirmPassword\":[\"**\"]}";
Assert.assertEquals(expected, replacePassword(input, "**"));
}
这是一个不同的解决方案,它还允许在多个匹配项中替换单个组。它使用堆栈来反转执行顺序,因此可以安全地执行字符串操作。
private static void demo () {
final String sourceString = "hello world!";
final String regex = "(hello) (world)(!)";
final Pattern pattern = Pattern.compile(regex);
String result = replaceTextOfMatchGroup(sourceString, pattern, 2, world -> world.toUpperCase());
System.out.println(result); // output: hello WORLD!
}
public static String replaceTextOfMatchGroup(String sourceString, Pattern pattern, int groupToReplace, Function<String,String> replaceStrategy) {
Stack<Integer> startPositions = new Stack<>();
Stack<Integer> endPositions = new Stack<>();
Matcher matcher = pattern.matcher(sourceString);
while (matcher.find()) {
startPositions.push(matcher.start(groupToReplace));
endPositions.push(matcher.end(groupToReplace));
}
StringBuilder sb = new StringBuilder(sourceString);
while (! startPositions.isEmpty()) {
int start = startPositions.pop();
int end = endPositions.pop();
if (start >= 0 && end >= 0) {
sb.replace(start, end, replaceStrategy.apply(sourceString.substring(start, end)));
}
}
return sb.toString();
}
不定期副业成功案例分享
m.replaceFirst("number $2$1");
应该是m.replaceFirst("number $3$1");