1. 概述

在文本处理中,从特定模式中提取内容是常见需求。当处理使用方括号封装重要信息的数据时,提取方括号内的文本可能会成为挑战。

本文将探讨提取方括号内文本的技术和方法,主要使用Java正则表达式(regex)作为核心解决方案。

2. 问题背景

为简化问题,我们先做两个前提假设:

  • 无嵌套方括号对 —— 例如 "[value1 [value2]]" 这样的输入不会出现
  • 方括号总是成对出现 —— 例如 "[value1..." 属于无效输入

方括号包裹的数据通常分两种场景:

  • 单对方括号输入:如 "some text [value] something else"
  • 多对方括号输入:如 "[value1] [value2] [value3]..."

我们将先解决单对方括号场景,再扩展到多对场景。核心工具:Java正则表达式

3. 单对方括号输入

给定输入示例:

String INPUT1 = "some text [THE IMPORTANT MESSAGE] something else";

目标提取内容:

String EXPECTED1 = "THE IMPORTANT MESSAGE";

3.1. \[.*\] 模式

直接思路是提取 [] 之间的内容,自然想到正则 \[.*\]。但需注意:

  • ⚠️ [] 在正则中用于定义字符类(如 [0-9] 匹配数字)
  • 必须转义才能匹配字面量 []

通过捕获组简化提取:

String result = null;
String rePattern = "\\[(.*)]";  // 转义[,]无需转义
Pattern p = Pattern.compile(rePattern);
Matcher m = p.matcher(INPUT1);
if (m.find()) {
    result = m.group(1);  // 获取捕获组内容
}
assertThat(result).isEqualTo(EXPECTED1);

踩坑提示:只需转义 [。因为 ] 前没有未转义的 [ 时,正则引擎会将其视为字面量。

3.2. 使用NOR字符类

另一种思路:方括号内内容 = 非 ] 的字符序列。利用NOR字符类 [^]] 实现:

String result = null;
String rePattern = "\\[([^]]*)";  // 匹配[后跟任意非]字符
Pattern p = Pattern.compile(rePattern);
Matcher m = p.matcher(INPUT1);
if (m.find()) {
    result = m.group(1);
}
assertThat(result).isEqualTo(EXPECTED1);

3.3. 使用 split() 方法

Java的 String.split() 支持正则分隔符。对于 "prefix[value]suffix"

  • [] 分割后得到数组:{"prefix", "value", "suffix"}
  • 取中间元素(索引1)即为目标值
String[] strArray = INPUT1.split("[\\[\\]]");  // 分割[和]
String result = strArray.length == 3 ? strArray[1] : null;
assertThat(result).isEqualTo(EXPECTED1);

踩坑警告:当输入以 ] 结尾时(如 "[value]"),默认 split() 会丢弃尾部空字符串:

String[] strArray = "[THE IMPORTANT MESSAGE]".split("[\\[\\]]");
assertThat(strArray).hasSize(2)
  .containsExactly("", "THE IMPORTANT MESSAGE");  // 缺少尾部空元素

解决方案:传入负数 limit 保留空元素:

String[] strArray = INPUT1.split("[\\[\\]]", -1);  // 保留空元素
String result = strArray.length == 3 ? strArray[1] : null;
...

4. 多对方括号输入

新输入示例:

final String INPUT2 = "[La La Land], [The last Emperor], and [Life of Pi] are all great movies.";

目标提取结果:

final List<String> EXPECTED2 = Lists.newArrayList("La La Land", "The last Emperor", "Life of Pi");

4.1. \[(.*)\] 模式——非贪婪版本

直接使用单对方案会失败,因为正则默认贪婪匹配

// 错误示例:贪婪匹配会捕获 "[La La Land], [The last Emperor], [Life of Pi"
String rePattern = "\\[(.*)]"; 

解决方案:添加 ? 启用非贪婪匹配,并用循环提取所有匹配:

List<String> result = new ArrayList<>();
String rePattern = "\\[(.*?)]";  // 非贪婪匹配
Pattern p = Pattern.compile(rePattern);
Matcher m = p.matcher(INPUT2);
while (m.find()) {  // 循环提取所有匹配
    result.add(m.group(1));
}
assertThat(result).isEqualTo(EXPECTED2);

4.2. 使用字符类

NOR字符类方案同样适用,只需将 if 改为 while 循环:

List<String> result = new ArrayList<>();
String rePattern = "\\[([^]]*)";  // NOR字符类
Pattern p = Pattern.compile(rePattern);
Matcher m = p.matcher(INPUT2);
while (m.find()) {
    result.add(m.group(1));
}
assertThat(result).isEqualTo(EXPECTED2);

4.3. 使用 split() 方法

多对方括号输入的分割结果示例:

输入: "---[value1]---[value2]---[value3]---"
数组: "---", "value1", "---", "value2", "---", "value3", "---"
索引: [0]     [1]      [2]     [3]      [4]     [5]      [6]

核心规律:所有奇数索引(1,3,5...)的元素为目标值:

List<String> result = new ArrayList<>();
String[] strArray = INPUT2.split("[\\[\\]]", -1);  // 保留空元素
for (int i = 1; i < strArray.length; i += 2) {  // 遍历奇数索引
    result.add(strArray[i]);
}
assertThat(result).isEqualTo(EXPECTED2);

5. 总结

本文系统介绍了Java中提取方括号内文本的多种方案:

  • ✅ 正则表达式方案(贪婪/非贪婪模式 + NOR字符类)
  • split() 方案(需处理边界情况)

核心要点:

  1. 单对方括号场景:直接捕获组或NOR字符类即可
  2. 多对方括号场景:需启用非贪婪匹配或循环提取
  3. split() 方案需注意尾部空元素处理

源码获取:完整示例代码见 GitHub仓库


原始标题:Extract Text Between Square Brackets