
引言
在数据处理和文本分析中,我们经常需要从复杂的字符串中提取特定的信息。一个常见的需求是从包含字母、数字和特殊字符的混合文本中识别并提取数字。然而,当需求变得更加精细时,例如要求将由非空白字符连接的数字视为一个连续的数字序列,而将由空白字符分隔的数字视为独立的序列,传统的正则表达式方法可能会显得力不从心。
例如,对于字符串 ds[44]%6c,我们期望提取 446。而对于 2021 ds[44]%6c,我们期望提取 2021 和 446。本教程将介绍两种基于Java的解决方案,利用正则表达式和Stream API高效地实现这一目标。
解决方案一:使用 Matcher.results() 捕获数字块 (Java 9+)
此方法通过构建一个能够捕获包含数字且两侧由零个或多个非空白字符包围的序列的正则表达式,然后利用Java 9引入的 Matcher.results() 方法来获取所有匹配项。
1. 正则表达式分析
我们将使用以下正则表达式:[^\\s]*\\d+[^\\s]*
- [^\\s]*:匹配零个或多个非空白字符。这部分用于捕获数字左侧的非数字、非空白字符,确保它们与数字一起被视为一个整体。
- \\d+:匹配一个或多个数字。这是我们想要提取的核心数字部分。
- [^\\s]*:再次匹配零个或多个非空白字符。这部分用于捕获数字右侧的非数字、非空白字符,同样确保它们与数字一起被视为一个整体。
这个正则表达式的整体作用是捕获一个“块”,这个块中至少包含一个数字,并且整个块不包含任何空白字符。
立即学习“Java免费学习笔记(深入)”;
2. 实现步骤
- 定义一个 Pattern 对象,编译上述正则表达式。
- 对输入字符串使用 Pattern.matcher() 创建 Matcher 对象。
- 调用 Matcher.results() 获取一个 Stream
。每个 MatchResult 代表一个完整的匹配项。 - 通过 map(MatchResult::group) 提取每个匹配项的完整字符串。
- 对每个提取出的字符串,使用 replaceAll("\\D+", "") 移除所有非数字字符,只保留纯数字。
- 将纯数字字符串通过 map(Integer::valueOf) 转换为 Integer 类型。
- 最后,使用 toList() 将结果收集到一个 List
中。
3. 示例代码
import java.util.List;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class NumberExtractor {
// 定义一个正则表达式,用于捕获包含至少一个数字,且前后由零个或多个非空白字符包围的序列
public static final Pattern TEXT_WITH_DIGITS = Pattern.compile("[^\\s]*\\d+[^\\s]*");
/**
* 从字符串中提取符合特定规则的数字序列。
* 数字序列由非空白字符连接时被视为一个整体,由空白字符分隔时被视为独立序列。
*
* @param str 待处理的输入字符串
* @return 提取出的数字序列列表
*/
public static List getIntsUsingMatcherResults(String str) {
return TEXT_WITH_DIGITS.matcher(str).results() // 获取所有匹配项的Stream
.map(MatchResult::group) // 提取每个匹配项的完整字符串
.map(s -> s.replaceAll("\\D+", "")) // 移除字符串中的所有非数字字符
.map(Integer::valueOf) // 将纯数字字符串转换为Integer
.collect(Collectors.toList()); // 收集结果到List
}
// ... (后续将添加 main 方法和另一个解决方案)
} 4. 注意事项
- 此方法需要 Java 9 或更高版本,因为 Matcher.results() 是 Java 9 中引入的。
- 正则表达式 [^\\s]*\\d+[^\\s]* 确保了匹配到的块内部不会包含空白字符,从而满足了由非空白字符连接的数字视为一个整体的要求。
解决方案二:使用 Pattern.splitAsStream() 按空白符分割 (Java 8+)
此方法采取不同的策略:首先根据空白字符将整个字符串分割成多个子串,然后对每个子串进行处理以提取其中的数字。
10分钟内自己学会PHP其中,第1篇为入门篇,主要包括了解PHP、PHP开发环境搭建、PHP开发基础、PHP流程控制语句、函数、字符串操作、正则表达式、PHP数组、PHP与Web页面交互、日期和时间等内容;第2篇为提高篇,主要包括MySQL数据库设计、PHP操作MySQL数据库、Cookie和Session、图形图像处理技术、文件和目录处理技术、面向对象、PDO数据库抽象层、程序调试与错误处理、A
1. 正则表达式分析
我们将使用以下正则表达式进行分割:\\s+
- \\s+:匹配一个或多个空白字符。这将作为我们的分隔符,将字符串拆分成多个不含空白字符的片段。
2. 实现步骤
- 定义一个 Pattern 对象,编译正则表达式 \\s+。
- 对输入字符串调用 Pattern.splitAsStream(str),这将生成一个 Stream
,其中每个元素都是按空白符分割后的子串。 - 处理起始空白符的边缘情况:如果输入字符串以空白符开头,splitAsStream() 会在Stream的第一个位置生成一个空字符串。为了避免这种情况,我们使用 dropWhile(String::isEmpty) 来跳过所有开头的空字符串。
- 对每个子串,使用 replaceAll("\\D+", "") 移除所有非数字字符。
- 将纯数字字符串通过 map(Integer::valueOf) 转换为 Integer 类型。
- 最后,使用 toList() 将结果收集到一个 List
中。
3. 示例代码
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
// ... (NumberExtractor 类的其他部分)
public class NumberExtractor {
// ... (getIntsUsingMatcherResults 方法)
// 定义一个正则表达式,用于匹配一个或多个空白字符,作为分割符
public static final Pattern WHITE_SPACES = Pattern.compile("\\s+");
/**
* 从字符串中提取符合特定规则的数字序列。
* 该方法首先按空白符分割字符串,然后从每个片段中提取数字。
*
* @param str 待处理的输入字符串
* @return 提取出的数字序列列表
*/
public static List getIntsUsingSplitAsStream(String str) {
return WHITE_SPACES.splitAsStream(str) // 按空白符分割字符串,生成Stream
.dropWhile(String::isEmpty) // 跳过开头的空字符串(如果存在,例如字符串以空白符开头)
.map(s -> s.replaceAll("\\D+", "")) // 移除每个片段中的所有非数字字符
.map(Integer::valueOf) // 将纯数字字符串转换为Integer
.collect(Collectors.toList()); // 收集结果到List
}
public static void main(String[] args) {
System.out.println("--- 使用 Matcher.results() ---");
System.out.println("ds[44]%6c -> " + getIntsUsingMatcherResults("ds[44]%6c"));
System.out.println("2021 ds[44]%6c -> " + getIntsUsingMatcherResults("2021 ds[44]%6c"));
System.out.println(" abc 123 def 456 -> " + getIntsUsingMatcherResults(" abc 123 def 456 "));
System.out.println("no_digits_here -> " + getIntsUsingMatcherResults("no_digits_here"));
System.out.println("only_42 -> " + getIntsUsingMatcherResults("only_42"));
System.out.println("mixed123_456_chars -> " + getIntsUsingMatcherResults("mixed123_456_chars"));
System.out.println("\n--- 使用 Pattern.splitAsStream() ---");
System.out.println("ds[44]%6c -> " + getIntsUsingSplitAsStream("ds[44]%6c"));
System.out.println("2021 ds[44]%6c -> " + getIntsUsingSplitAsStream("2021 ds[44]%6c"));
System.out.println(" abc 123 def 456 -> " + getIntsUsingSplitAsStream(" abc 123 def 456 "));
System.out.println("no_digits_here -> " + getIntsUsingSplitAsStream("no_digits_here"));
System.out.println("only_42 -> " + getIntsUsingSplitAsStream("only_42"));
System.out.println("mixed123_456_chars -> " + getIntsUsingSplitAsStream("mixed123_456_chars"));
}
} 4. 注意事项
- 此方法需要 Java 8 或更高版本,因为 Pattern.splitAsStream() 和 Stream.dropWhile() (Java 9+) 或其他Stream操作是 Java 8/9 中引入的。dropWhile本身是Java 9引入,如果要在Java 8中使用,需要手动过滤空字符串,例如 filter(s -> !s.isEmpty())。
- Pattern.splitAsStream() 相较于 String.split() 的优势在于,它直接生成一个Stream,避免了创建中间数组的开销,对于处理大型字符串时可能更高效。
运行结果
执行上述 main 方法,将得到如下输出:
--- 使用 Matcher.results() --- ds[44]%6c -> [446] 2021 ds[44]%6c -> [2021, 446] abc 123 def 456 -> [123, 456] no_digits_here -> [] only_42 -> [42] mixed123_456_chars -> [123456] --- 使用 Pattern.splitAsStream() --- ds[44]%6c -> [446] 2021 ds[44]%6c -> [2021, 446] abc 123 def 456 -> [123, 456] no_digits_here -> [] only_42 -> [42] mixed123_456_chars -> [123456]
可以看到,两种方法都成功地实现了预期的数字提取逻辑。
总结
本文详细介绍了在Java中利用正则表达式和Stream API从混合字符串中提取特定数字序列的两种有效方法。
- Matcher.results() 方法 (Java 9+):通过一个精巧的正则表达式 [^\\s]*\\d+[^\\s]* 直接匹配不含空白字符的数字块,然后通过Stream操作清理非数字字符并转换为整数。这种方法在概念上更直接,因为它一次性捕获了符合条件的“数字单元”。
- Pattern.splitAsStream() 方法 (Java 8+):首先利用 \\s+ 正则表达式按空白符将字符串分割成多个片段,然后对每个片段进行数字提取和转换。此方法在处理以空白符开头的字符串时需要额外的 dropWhile (或 filter) 处理。
选择哪种方法取决于您的Java版本偏好以及对代码可读性的考量。两者都能高效地解决将由非空白字符连接的数字视为一个整体,而由空白字符分隔的数字视为独立序列的问题。在实际应用中,应根据具体场景和性能要求选择最合适的方案。









