This question already has answers here:
Regex: match only outside parenthesis (so that the text isn't split within parenthesis)?
                                
                                    (2个答案)
                                
                        
                4个月前关闭。
            
        

我(Regex noob)试图对包含某些模式的字符串执行替换操作。例如

AAA-BBB-CCC-{AAA-BBB-AAA-BBB}-CCC-BBB-AAA

在上面,我试图将所有A替换为I,但忽略大括号内的A

为此,我可以做的是在模式上拆分整个字符串,然后执行替换,然后将字符串连接起来。

我想知道正则表达式中是否有更短的方法,以便我可以执行类似

String str = "AAA-BBB-CCC-{AAA-BBB-AAA-BBB}-CCC-BBB-AAA";
str = str.replaceButIgnorePattern("A", "I","\\{(.*?)\\}");
System.out.print(str);   //III-BBB-CCC-{AAA-BBB-AAA-BBB}-CCC-BBB-III


图案可以像


包含任何字符
可以在字符串的开头,中间或结尾

最佳答案

考虑到没有嵌套的花括号,一种解决方案是匹配最接近的{}内的子字符串,并匹配并捕获要替换的模式,然后检查Group 1是否不为null,然后采取相应措施。

在Java 9+中,您可以使用

String text = "AAA-BBB-CCC-{AAA-BBB-AAA-BBB}-CCC-BBB-AAA";
Pattern r = Pattern.compile("\\{[^{}]*}|(A)");
Macher m = r.matcher(text);
String result = m.replaceAll(x -> x.group(1) != null ? "I" : x.group() );
System.out.println( result );


请参见online demo

在这里,\{[^{}]*}匹配{,除{}之外的任何0+字符,然后}或(|)将A捕获到组1中。

较旧的Java版本的等效代码:

String text = "AAA-BBB-CCC-{AAA-BBB-AAA-BBB}-CCC-BBB-AAA";
Pattern r = Pattern.compile("\\{[^{}]*}|(A)");
Matcher m = r.matcher(text);
StringBuffer sb = new StringBuffer();
while (m.find()) {
    if (m.group(1) == null) {
        m.appendReplacement(sb,  m.group(0));
    } else {
        m.appendReplacement(sb,  "I");
    }
}
m.appendTail(sb);
System.out.println(sb);


请参见online Java demo

您还可以对任何Java版本使用通用的解决方法:

str = str.replaceAll("A(?![^{}]*})", "I");


其中(?![^{}]*})确保{}没有出现0+次,紧随其后的是当前位置右侧的}。注意此方法意味着该字符串包含平衡数量的打开/关闭大括号。

08-18 06:15
查看更多