Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/regex/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
我可以替换Java正则表达式中的组吗?_Java_Regex_Replace_Regex Group - Fatal编程技术网

我可以替换Java正则表达式中的组吗?

我可以替换Java正则表达式中的组吗?,java,regex,replace,regex-group,Java,Regex,Replace,Regex Group,我有这段代码,我想知道,我是否可以在Java正则表达式中只替换组(而不是所有模式)。 代码: 通过在*周围添加paren来添加第三个组,然后将子序列替换为“number”+m.group(2)+“1”。e、 g: String output = m.replaceFirst("number" + m.group(2) + "1"); 使用$n(其中n是数字)引用中捕获的子序列。我假设您想用文字字符串“number”替换第一个组,用第一个组的值替换第二个组 Pattern p = Pattern

我有这段代码,我想知道,我是否可以在Java正则表达式中只替换组(而不是所有模式)。 代码:


通过在
*
周围添加paren来添加第三个组,然后将子序列替换为
“number”+m.group(2)+“1”
。e、 g:

String output = m.replaceFirst("number" + m.group(2) + "1");
使用
$n
(其中n是数字)引用中捕获的子序列。我假设您想用文字字符串“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()和matcher.end()方法来获取组位置。因此,使用此位置,您可以轻松替换任何文本。

您可以使用并构建通用替换方法:

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, "!!!"));
}
publicstaticstringreplacegroup(stringregex、stringsource、int-groupToReplace、stringreplacement){
返回replaceGroup(regex,source,groupToReplace,1,replacement);
}
公共静态字符串替换组(字符串正则表达式、字符串源、int-groupToReplace、int-groupOccurrence、字符串替换){
Matcher m=Pattern.compile(regex.Matcher)(源代码);
对于(int i=0;i

检查

这里有一个不同的解决方案,它还允许在多个匹配中替换单个组。 它使用堆栈反转执行顺序,因此可以安全地执行字符串操作

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();       
}
私有静态void演示(){
最后一个字符串sourceString=“hello world!”;
最后一个字符串regex=“(你好)(世界)(!)”;
最终模式=Pattern.compile(regex);
String result=replaceTextOfMatchGroup(sourceString,pattern,2,world->world.toUpperCase());
System.out.println(result);//输出:hello WORLD!
}
公共静态字符串replaceTextOfMatchGroup(字符串源字符串、模式模式、int-groupToReplace、函数replaceStrategy){
堆栈起始位置=新堆栈();
堆栈结束位置=新堆栈();
Matcher Matcher=pattern.Matcher(sourceString);
while(matcher.find()){
startPositions.push(matcher.start(groupToReplace));
结束位置。推(匹配器。结束(组替换));
}
StringBuilder sb=新的StringBuilder(sourceString);
而(!startPositions.isEmpty()){
int start=startPositions.pop();
int end=endPositions.pop();
如果(开始>=0和结束>=0){
sb.replace(start,end,replacetstrategy.apply(sourceString.substring(start,end)));
}
}
使某人返回字符串();
}

很抱歉打了一匹死马,但没有人指出这一点有点奇怪——“是的,你可以,但这与你在现实生活中使用抓捕群的方式相反”

如果按照预期的方式使用正则表达式,解决方案如下:

"6 example input 4".replaceAll("(?:\\d)(.*)(?:\\d)", "number$11");
或者正如下面shmosel正确指出的

"6 example input 4".replaceAll("\d(.*)\d", "number$11");
…因为在你的正则表达式中根本没有很好的理由对小数进行分组

您通常不会在要丢弃的字符串部分使用捕获组,而是在要保留的字符串部分使用它们

如果您确实想要替换组,那么您可能需要的是一个模板引擎(例如,小胡子、ejs、StringTemplate等)


对于好奇的人来说,即使是正则表达式中的非捕获组也只是为了满足正则表达式引擎需要它们识别和跳过变量文本的情况。例如,在

(?:abc)*(capture me)(?:bcd)*
如果您的输入看起来像“abcabccapture mebcdbcd”或“abccapture mebcd”,甚至只是“capture me”,则需要它们


或者换一种说法:如果文本总是相同的,而您没有捕获它,则根本没有理由使用组。

替换输入中的密码字段:

{"_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, "**"));
  }

实际上,Matcher支持$2的引用方式,所以m.replaceFirst(“数字$21”)也会做同样的事情。事实上,它们做的不是同样的事情<代码>“数字$21”
有效,而
“数字”+m.group(2)+“1”
无效。看起来
数字$21
将取代第21组,而不是第2组+字符串“1”。这是纯字符串串联,对吗?为什么我们需要先调用replaceFirst?你能澄清你的问题吗,比如可能会给出输入的预期输出?如果你在第一次匹配时发布一个输出他的作品的示例,那会很好,但是如果有很多组,并且你在一段时间内对它们进行迭代(m.find())则不起作用。我同意Hugo,这是一种实施解决方案的糟糕方式。。。究竟为什么这是公认的答案而不是acdcjunior的答案——这是一个完美的解决方案:代码量少,内聚性高,耦合性低,产生不必要副作用的可能性(如果不是没有机会的话)要小得多。。。唉……这个答案目前是无效的。
m.replaceFirst(“数字$2$1”)应为
m.replaceFirst(“数字$3$1”)这真的应该是公认的答案,它是最完整和“随时可用”的解决方案,不会给accompa引入耦合级别
{"_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, "**"));
  }