本文介绍一种高效、可靠的 Java 正则表达式方案,用于按连字符 - 分割字符串,但严格跳过嵌套在 {{...}} 内部的连字符,仅匹配外部可见的分隔符,并完整保留花括号包裹的子串。
本文介绍一种高效、可靠的 java 正则表达式方案,用于按连字符 `-` 分割字符串,但严格跳过嵌套在 `{{...}}` 内部的连字符,仅匹配外部可见的分隔符,并完整保留花括号包裹的子串。
在实际开发中,常需处理结构化标记字符串(如模板语法、DSL 片段),其中 {{...}} 表示占位符或表达式块,内部的 - 应视为内容而非分隔符。例如:
- {{abc-def}}-123-benefit-{{ghi}} 应拆分为 ["{{abc-def}}", "123", "benefit", "{{ghi}}"];
- abc-{{123-def}}-benefit-{{ghi}} 应拆分为 ["abc", "{{123-def}}", "benefit", "{{ghi}}"]。
此时,不能使用 String.split() 配合“否定型断言”类正则(如 (?!...)),因为连字符的位置依赖于其是否处于 {{...}} 的上下文之外——而标准正则无法可靠“记忆”括号嵌套状态(Java 正则不支持递归匹配)。更稳健的思路是:不以“分割”为出发点,转而采用“匹配提取”策略——即用正则主动捕获所有合法片段(花括号块 或 非括号非连字符序列),从而自然绕过分隔逻辑。
推荐正则表达式如下:
String pattern = "(?:\{\{.*?\}\}|[^{}-]+)";该模式含义清晰:
- (?: ... | ... ):非捕获分组,匹配两种情形之一;
- \{\{.*?\}\}:匹配最短的 {{...}} 块(.*? 实现懒惰匹配,避免跨块吞并);
- |:或;
- [^{}-]+:匹配一个或多个既不是 {、也不是 }、也不是 - 的字符(即安全的“普通片段”,天然以 - 为边界)。
✅ 完整 Java 示例代码:
import java.util.*;
import java.util.regex.*;
public class CurlyBraceAwareSplit {
public static List<String> splitOutsideBraces(String input) {
String pattern = "(?:\{\{.*?\}\}|[^{}-]+)";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(input);
List<String> result = new ArrayList<>();
while (m.find()) {
result.add(m.group());
}
return result;
}
public static void main(String[] args) {
// 测试用例
System.out.println(splitOutsideBraces("{{abc-def}}-123-benefit-{{def}}"));
// 输出: [{{abc-def}}, 123, benefit, {{def}}]
System.out.println(splitOutsideBraces("abc-{{123-def}}-benefit-{{ghi}}"));
// 输出: [abc, {{123-def}}, benefit, {{ghi}}]
System.out.println(splitOutsideBraces("a-{{b-c}}-d-{{e-f-g}}-h"));
// 输出: [a, {{b-c}}, d, {{e-f-g}}, h]
}
}⚠️ 注意事项:
- 不支持嵌套花括号:本方案假设 {{...}} 是扁平结构(即不会出现 {{{nested}}})。若需支持任意嵌套,须改用解析器(如栈扫描),正则无法胜任;
- 空片段处理:输入首尾或连续 -(如 "a--b")会产生空字符串,可根据业务需要添加 .filter(s -> !s.isEmpty()) 过滤;
- 特殊字符转义:{, }, - 在字符类 [^{}-] 中无需额外转义(- 放末尾即字面量),但 \{\{ 外部需双反斜杠(Java 字符串转义 + 正则引擎转义);
- 性能提示:对超长文本,建议预编译 Pattern(如示例所示),避免重复编译开销。
总结:面对“条件性分割”需求,优先考虑“正向匹配”而非“负向分割”。该正则简洁、可读性强、无回溯风险,是处理此类模板化字符串的工业级实践方案。










