Regexp删除嵌套的括号

我一直试图用Java编写正则表达式来删除下面括号中的所有内容,同时保留其他所有内容。 请注意,括号可以嵌套,我认为这就是我的模式失败的原因 。 有人能帮我吗? 下面我试过:

String testData = "1. d4 Nf6 2. c4 g6 3. Nc3 Bg7 4. e4 d6 5. Nf3 OO 6. Be2 e5 7. dxe5 dxe5 8. Qxd8 Rxd8 9. Bg5 Nbd7 10. OOO {Diagram [#]} " + "Rf8 (10... Re8 11. Nb5 (11. Nd5)) (10... h6 11. Bxf6 Bxf6 12. Nd5) 11. Nd5 c6 (11... Nxe4 12. Nxc7 Rb8 13. Be3 b6 ) 12. Ne7+ Kh8 13. " + "Nxc8 Raxc8 14. Bxf6 (14. Be3) 14... Nxf6 15. Nd2 (15. Bd3) 15... Bh6 16. f3 Nd7 17. Kc2 Bxd2 (17... Rcd8 18. b4) 18. Rxd2 Nc5 19. b4 Ne6 20. Rd7 b5 " + "(20... Rcd8 21. Rxb7 Nd4+ 22. Kd3) 21. Rxa7 Nd4+ 22. Kd3 Rcd8 23. Ke3 Nc2+ 24. Kf2 Rd2 25. Rd1 Rfd8 26. Rxd2 {Diagram [#]} (26. cxb5 cxb5 " + "27. Rc7 Rxd1 28. Bxd1 Rd2+ 29. Kg3 Ne1 30. Bb3 f6 31. Rf7 Nxg2 32. Rf8+ Kg7 33. Rf7+ Kh6 34. Rxf6 Nf4 35. Kh4 (35. Rxf4 exf4+ 36. Kxf4 Rxh2) 35... " + "Rxh2+ 36. Kg4 Rg2+ 37. Kh4 Nd3 38. a3 Rh2+ 39. Kg4 Rh1 40. Rc6 {Diagram [#]}) 26... Rxd2 27. Kf1 Nd4 28. cxb5 cxb5 29. a4 (29. Rd7 Rxa2 30. Bd3 Ra3 31. " + "Be2 Ra1+ 32. Kf2 Ra2 ) (29. Bxb5 Nxb5) 29... Rxe2 (29... bxa4 30. Bc4) 30. axb5 Rb2 31. b6 Rxb4 32. b7 Kg7 "; testData = testData.replaceAll(Pattern.quote("{") + ".*" + Pattern.quote("}"), "") .replaceAll(Pattern.quote("(") + ".*" + Pattern.quote(")"), "") .replaceAll(Pattern.quote("$") + "[0-9]+", ""); System.out.println(testData); 

但这打印:

  1. d4 Nf6 2. c4 g6 3. Nc3 Bg7 4. e4 d6 5. Nf3 O 6. Be2 e5 7. dxe5 dxe5 8. Qxd8 Rxd8 9. Bg5 Nbd7 10. OOO Rf8)11。Nd5 c6 12. Ne7 + Kh8 13. Nxc8 Raxc8 14. Bxf6 14 … Nxf6 15. Nd2 15 … Bh6 16. f3 Nd7 17.Kc2 Bxd2 18. Rxd2 Nc5 19. b4 Ne6 20. Rd7 b5 21. Rxa7 Nd4 + 22.Kd3 Rcd8 23.Ke3 Nc2 + 24 Kf2 Rd2 25. Rd1 Rfd8 26. Rxd2 35 … Rxh2 + 36.Kg4 Rg2 + 37.Kh4 Nd3 38. a3 Rh2 + 39.Kg4 Rh1 40. Rc6)26 … Rxd2 27.Kf1 Nd4 28.cxb5cxb5 29。 a4 29 … Rxe2 30. axb5 Rb2 31. b6 Rxb4 32. b7 Kg7

这显然是错误的,因为它有括号。

正确答案是:

  1. d4 Nf6 2. c4 g6 3. Nc3 Bg7 4. e4 d6 5. Nf3 O 6. Be2 e5 7. dxe5 dxe5 8. Qxd8 Rxd8 9. Bg5 Nbd7 10. OOO Rf8 11. Nd5 c6 12. Ne7 + Kh8 13. Nxc8 Raxc8 14. Bxf6 14 … Nxf6 15. Nd2 15 … Bh6 16.f3 Nd7 17.Kc2 Bxd2 18. Rxd2 Nc5 19. b4 Ne6 20. Rd7 b5 21. Rxa7 Nd4 + 22.Kd3 Rcd8 23.Ke3 Nc2 + 24。 Kf2 Rd2 25. Rd1 Rfd8 26. Rxd2 26 … Rxd2 27. Kf1 Nd4 28.cxb5 cxb5 29. a4 29 … Rxe2 30. axb5 Rb2 31. b6 Rxb4 32. b7 Kg7

不要在这里使用正则表达式。 从你的例子中可以看出像\\(.*?)\\)类的东西会尝试找到第一个创建(和下一个)之间的最小匹配,所以在类似数据的情况下

 a (b (cd) e) f 

正则表达式\(.*?\)将匹配

 a (b (cd) e) f ^^^^^^^^ 

并将留下e)部分无与伦比。

您可能可以为此任务编写正则表达式,因为一些正则表达式支持递归,但遗憾的是,Java中使用的正则表达式引擎不支持递归。

因此,要删除嵌套括号,您可以编写自己的简单解析器,例如
(我假设文本格式正确,所以没有像({)}或unclosed括号这样的东西)

 String data = "1. d4 Nf6 2. c4 g6 3. Nc3 Bg7 4. e4 d6 5. Nf3 OO 6. Be2 e5 7. dxe5 dxe5 8. Qxd8 Rxd8 9. Bg5 Nbd7 10. OOO {Diagram [#]} " + "Rf8 (10... Re8 11. Nb5 (11. Nd5)) (10... h6 11. Bxf6 Bxf6 12. Nd5) 11. Nd5 c6 (11... Nxe4 12. Nxc7 Rb8 13. Be3 b6 ) 12. Ne7+ Kh8 13. " + "Nxc8 Raxc8 14. Bxf6 (14. Be3) 14... Nxf6 15. Nd2 (15. Bd3) 15... Bh6 16. f3 Nd7 17. Kc2 Bxd2 (17... Rcd8 18. b4) 18. Rxd2 Nc5 19. b4 Ne6 20. Rd7 b5 " + "(20... Rcd8 21. Rxb7 Nd4+ 22. Kd3) 21. Rxa7 Nd4+ 22. Kd3 Rcd8 23. Ke3 Nc2+ 24. Kf2 Rd2 25. Rd1 Rfd8 26. Rxd2 {Diagram [#]} (26. cxb5 cxb5 " + "27. Rc7 Rxd1 28. Bxd1 Rd2+ 29. Kg3 Ne1 30. Bb3 f6 31. Rf7 Nxg2 32. Rf8+ Kg7 33. Rf7+ Kh6 34. Rxf6 Nf4 35. Kh4 (35. Rxf4 exf4+ 36. Kxf4 Rxh2) 35... " + "Rxh2+ 36. Kg4 Rg2+ 37. Kh4 Nd3 38. a3 Rh2+ 39. Kg4 Rh1 40. Rc6 {Diagram [#]}) 26... Rxd2 27. Kf1 Nd4 28. cxb5 cxb5 29. a4 (29. Rd7 Rxa2 30. Bd3 Ra3 31. " + "Be2 Ra1+ 32. Kf2 Ra2 ) (29. Bxb5 Nxb5) 29... Rxe2 (29... bxa4 30. Bc4) 30. axb5 Rb2 31. b6 Rxb4 32. b7 Kg7 "; StringBuilder buffer = new StringBuilder(); int parenthesisCounter = 0; for (char c : data.toCharArray()) { if (c == '(' || c == '{' ) parenthesisCounter++; if (c == ')' || c == '}' ) parenthesisCounter--; if (!(c == '(' || c == '{' || c == ')' || c == '}') && parenthesisCounter == 0) buffer.append(c); } 

之后,您可以专注于删除之前使用过的其他不需要的数据

 .replaceAll(Pattern.quote("$") + "[0-9]+", ""); 

所以结果

 System.out.println(buffer.toString().replaceAll( Pattern.quote("$") + "[0-9]+", "")); 

将会

1. d4 Nf6 2. c4 g6 3. Nc3 Bg7 4. e4 d6 5. Nf3 OO 6. Be2 e5 7. dxe5 dxe5 8. Qxd8 Rxd8 9. Bg5 Nbd7 10. OOO Rf8 11. Nd5 c6 12. Ne7+ Kh8 13. Nxc8 Raxc8 14. Bxf6 14... Nxf6 15. Nd2 15... Bh6 16. f3 Nd7 17. Kc2 Bxd2 18. Rxd2 Nc5 19. b4 Ne6 20. Rd7 b5 21. Rxa7 Nd4+ 22. Kd3 Rcd8 23. Ke3 Nc2+ 24. Kf2 Rd2 25. Rd1 Rfd8 26. Rxd2 26... Rxd2 27. Kf1 Nd4 28. cxb5 cxb5 29. a4 29... Rxe2 30. axb5 Rb2 31. b6 Rxb4 32. b7 Kg7

Pshemo的答案很好,但我想告诉你如何使用正则表达式完成它以及我认为解析可以如何优化:

 import java.util.regex.Pattern; /** * Created for http://stackoverflow.com/a/25335225/1266906 */ public class RemoveBrackets { public static void main(String[] args) { String testData = "1. d4 Nf6 2. c4 g6 3. Nc3 Bg7 4. e4 d6 5. Nf3 OO 6. Be2 e5 7. dxe5 dxe5 8. Qxd8 Rxd8 9. Bg5 Nbd7 10. OOO {Diagram [#]} " + "Rf8 (10... Re8 11. Nb5 (11. Nd5)) (10... h6 11. Bxf6 Bxf6 12. Nd5) 11. Nd5 c6 (11... Nxe4 12. Nxc7 Rb8 13. Be3 b6 ) 12. Ne7+ Kh8 13. " + "Nxc8 Raxc8 14. Bxf6 (14. Be3) 14... Nxf6 15. Nd2 (15. Bd3) 15... Bh6 16. f3 Nd7 17. Kc2 Bxd2 (17... Rcd8 18. b4) 18. Rxd2 Nc5 19. b4 Ne6 20. Rd7 b5 " + "(20... Rcd8 21. Rxb7 Nd4+ 22. Kd3) 21. Rxa7 Nd4+ 22. Kd3 Rcd8 23. Ke3 Nc2+ 24. Kf2 Rd2 25. Rd1 Rfd8 26. Rxd2 {Diagram [#]} (26. cxb5 cxb5 " + "27. Rc7 Rxd1 28. Bxd1 Rd2+ 29. Kg3 Ne1 30. Bb3 f6 31. Rf7 Nxg2 32. Rf8+ Kg7 33. Rf7+ Kh6 34. Rxf6 Nf4 35. Kh4 (35. Rxf4 exf4+ 36. Kxf4 Rxh2) 35... " + "Rxh2+ 36. Kg4 Rg2+ 37. Kh4 Nd3 38. a3 Rh2+ 39. Kg4 Rh1 40. Rc6 {Diagram [#]}) 26... Rxd2 27. Kf1 Nd4 28. cxb5 cxb5 29. a4 (29. Rd7 Rxa2 30. Bd3 Ra3 31. " + "Be2 Ra1+ 32. Kf2 Ra2 ) (29. Bxb5 Nxb5) 29... Rxe2 (29... bxa4 30. Bc4) 30. axb5 Rb2 31. b6 Rxb4 32. b7 Kg7 "; System.out.println(replaceLoop(testData).replaceAll(Pattern.quote("$") + "[0-9]+", "")); System.out.println(copyIterator(testData).replaceAll(Pattern.quote("$") + "[0-9]+", "")); } private static String replaceLoop(String data) { Pattern pattern = Pattern.compile("\\([^()]*\\)|\\{[^{}]*\\}"); String previous, current = data; do { previous = current; current = pattern.matcher(previous).replaceAll(""); } while (!previous.equals(current)); return current; } private static String copyIterator(String data) { StringBuilder sb = new StringBuilder(); int start = 0; int level = 0; for (int i = 0; i < data.length(); i++) { switch (data.charAt(i)) { case '(': case '{': if (level == 0 && start >= 0) { sb.append(data.substring(start, i)); start = -1; } level++; break; case ')': case '}': level--; if (level == 0) { start = i + 1; } else if (level < 0) { throw new IllegalStateException("Too many closing brackets."); } break; } } if (level > 0) { throw new IllegalStateException("Too many opening brackets."); } if (start >= 0 && start < data.length()) { sb.append(data.substring(start, data.length())); } return sb.toString(); } } 

replaceLoop我只删除不包含大括号的大括号组(内部大括号),因此必须重复,直到String不再更改为止。 这可能很昂贵,特别是如果你希望大括号嵌套。 如前所述,问题在于你只能引用已经匹配的字符,而不是它们的相反或它们的数量; 如果您知道通常嵌套的内容有多深,您当然可以编写一个模式,一次性删除所有预期的级别,并且很少需要两次以上的搜索。

copyIterator我确定哪些块没有被包含,并将这些块复制到新的StringBuilder 。 通过复制块我最小化StringBuilderresize的次数,并且复制块通常与复制单个字符相同,每个字符的成本降低。 同样通过使用switch ,编译器可以使用一个整数映射,它可以一次检查4个相关字符,而不是逐个检查它,就像我需要做的那样(是的,很酷的编译器应该这样做)对你而言......)