Java正则表达式终极指南:从入门到精通,轻松提取各种数字(附代码案例)
** 本文是Java程序员必备的实战指南,深入浅出地讲解如何使用正则表达式高效提取字符串中的数字,无论你是需要提取简单的整数、浮点数,还是处理复杂的科学计数法或带千分位的数字,本文都将提供精准的解决方案和可直接运行的代码示例,助你彻底掌握Java数字提取技巧。

引言:为什么Java正则表达式是提取数字的利器?
在数据处理和信息抓取的日常工作中,我们经常遇到从一个杂乱的字符串中提取出特定数字的需求。
- 从
“您的订单号是:12345,总金额为:¥998.50元”中提取订单号12345和金额50。 - 从
“CPU使用率:15.6%,内存占用:2048MB”中提取6和2048。 - 从
“产品版本:v2.1.0-beta3”中提取版本号2,1,0,3。
手动遍历字符串字符进行判断虽然可行,但代码冗长、效率低下且容易出错。Java正则表达式 便成为了我们最强大的工具,它以其简洁、高效和灵活的特性,能以极少的代码量完成复杂的模式匹配任务,是每一位Java开发者必须精通的核心技能。
本文将带你系统学习如何利用Java正则表达式,轻松应对各种数字提取场景。
Java正则表达式基础:核心元字符速览
在开始实战之前,我们先快速回顾一下与数字提取相关的核心正则表达式元字符,如果你已经熟悉,可以跳过本节,直接进入实战案例。

| 元字符 | 描述 | 示例 |
|---|---|---|
\d |
匹配任意一个数字字符(等同于 [0-9]) |
\d 可以匹配 1, 2, 9 |
\D |
匹配任意一个非数字字符(等同于 [^0-9]) |
\D 可以匹配 a, , 空格 |
| 匹配前面的子表达式一次或多次 | \d+ 可以匹配 123, 45 |
|
| 匹配前面的子表达式零次或多次 | \d* 可以匹配 123, 空 |
|
| 匹配前面的子表达式零次或一次 | \d? 可以匹配 1, 空 |
|
| 匹配除换行符以外的任意单个字符 | 2 可以匹配 12, 1a2 |
|
[] |
字符集,匹配方括号内的任意一个字符 | [135] 可以匹配 1, 3, 5 |
| 分组,将括号内的表达式作为一个整体 | (\d+) 将连续的数字视为一个组 |
|
^ |
匹配字符串的开始位置 | ^\d+ 匹配字符串开头的数字 |
| 匹配字符串的结束位置 | \d+$ 匹配字符串结尾的数字 |
|
\. |
转义字符,匹配 字符本身 | 3\.14 匹配 14 而不是 314 |
核心工具类:
java.util.regex.Pattern:正则表达式的编译表示。java.util.regex.Matcher:对输入字符串进行匹配操作的引擎。
实战案例:从简单到复杂,逐个击破
案例1:提取所有连续的整数
需求: 从字符串 "我有3个苹果,12个橙子,和253个香蕉。" 中提取所有整数。
分析: 我们需要找到所有由一个或多个数字组成的连续序列。\d+ 正是为此而生。
代码实现:
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ExtractIntegers {
public static void main(String[] args) {
String text = "我有3个苹果,12个橙子,和253个香蕉。";
// 1. 定义正则表达式:\d+ 表示一个或多个数字
String regex = "\\d+"; // 注意:在Java字符串中,\需要转义为\\
// 2. 编译正则表达式
Pattern pattern = Pattern.compile(regex);
// 3. 创建Matcher对象
Matcher matcher = pattern.matcher(text);
// 4. 查找所有匹配项
List<String> numbers = new ArrayList<>();
while (matcher.find()) {
// group() 方法返回匹配到的子序列
numbers.add(matcher.group());
}
// 5. 输出结果
System.out.println("原始文本: " + text);
System.out.println("提取到的整数: " + numbers); // 输出: [3, 12, 253]
}
}
代码解读:
Pattern.compile("\\d+"):将\d+编译成一个模式对象。pattern.matcher(text):创建一个匹配器,用于在text中查找模式。matcher.find():尝试查找下一个匹配子序列,如果找到,返回true。matcher.group():返回当前匹配到的子字符串。
案例2:提取所有浮点数(包括小数)
需求: 从字符串 "温度:23.5°C,湿度:65.78%,气压:1013.25hPa" 中提取所有浮点数。
分析: 浮点数由整数部分、小数点和可选的小数部分组成,我们可以用 \d+\.\d+ 来匹配。
\d+:匹配整数部分(一个或多个数字)。\.:匹配小数点本身( 是特殊字符,需要转义)。\d+:匹配小数部分(一个或多个数字)。
代码实现:
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ExtractDecimals {
public static void main(String[] args) {
String text = "温度:23.5°C,湿度:65.78%,气压:1013.25hPa";
// \d+\.\d+ 匹配一个或多个数字,后跟一个小数点,再跟一个或多个数字
String regex = "\\d+\\.\\d+";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
List<String> decimals = new ArrayList<>();
while (matcher.find()) {
decimals.add(matcher.group());
}
System.out.println("原始文本: " + text);
System.out.println("提取到的浮点数: " + decimals); // 输出: [23.5, 65.78, 1013.25]
}
}
案例3:提取所有数字(整数和浮点数)
需求: 合并案例1和案例2,从 "总价100元,单价25.5元,折扣0.8" 中提取所有数字。
分析: 我们需要一个能同时匹配 100 和 5 的模式,可以这样构建:
(\d+\.\d+|\d+)
- 表示“或”逻辑。
\d+\.\d+:匹配浮点数。\d+:匹配整数。- 整个表达式表示“匹配浮点数,或者匹配整数”。
代码实现:
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ExtractAllNumbers {
public static void main(String[] args) {
String text = "总价100元,单价25.5元,折扣0.8";
// 匹配整数或浮点数
String regex = "\\d+\\.\\d+|\\d+";
// 更简洁的写法:\\d*\\.?\\d+
// \d* 零个或多个数字
// \.? 零个或一个小数点
// \d+ 一个或多个数字
// 这个模式可以匹配 "123", "123.", ".123", "123.45"
// 但我们通常只想匹配有数字的,所以上面的第一种更精确
String regexOptimized = "\\d+(\\.\\d+)?"; // 匹配整数或带小数部分的浮点数
Pattern pattern = Pattern.compile(regexOptimized);
Matcher matcher = pattern.matcher(text);
List<String> allNumbers = new ArrayList<>();
while (matcher.find()) {
allNumbers.add(matcher.group());
}
System.out.println("原始文本: " + text);
System.out.println("提取到的所有数字: " + allNumbers); // 输出: [100, 25.5, 0.8]
}
}
注意: \d+(\\.\\d+)? 是一个更优且更常见的写法,它通过 创建一个分组, 表示这个分组(小数点部分)是可选的。
案例4:处理带千分位的数字(如:1,000,000)
需求: 从 "项目预算:1,500,000.50元" 中提取 50。
分析: 千分位由逗号分隔,我们需要匹配 1,500,000.50 这样的模式,并最终去除逗号,模式为 \d{1,3}(,\d{3})*(\.\d+)?
\d{1,3}:1到3位数字(如1或500)。(,\d{3})*:零次或多次的“逗号+3位数字”组合(如,000或,500,000)。(\.\d+)?:可选的小数部分。
代码实现:
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ExtractFormattedNumber {
public static void main(String[] args) {
String text = "项目预算:1,500,000.50元";
// 匹配带千分位的数字
String regex = "\\d{1,3}(,\\d{3})*(\\.\\d+)?";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
List<String> formattedNumbers = new ArrayList<>();
while (matcher.find()) {
formattedNumbers.add(matcher.group());
}
System.out.println("原始文本: " + text);
System.out.println("提取到的带千分位数字: " + formattedNumbers); // 输出: [1,500,000.50]
// 进阶:去除逗号,转换为纯数字
if (!formattedNumbers.isEmpty()) {
String formattedNum = formattedNumbers.get(0);
String pureNumber = formattedNum.replace(",", "");
System.out.println("去除逗号后的纯数字: " + pureNumber); // 输出: 1500000.50
}
}
}
案例5:提取特定格式的数字(如科学计数法 1.23E-10)
需求: 从 "引力常数 G = 6.67430E-11 (m³ kg⁻¹ s⁻²)" 中提取 67430E-11。
分析: 科学计数法格式为 [数字].[数字]E[正负号][数字],模式为 \d+\.\d+E[+-]?\d+
\d+\.\d+:尾数部分。E:指数部分的标志。[+-]?:可选的正号或负号。\d+:指数值。
代码实现:
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ExtractScientificNotation {
public static void main(String[] args) {
String text = "引力常数 G = 6.67430E-11 (m³ kg⁻¹ s⁻²)";
// 匹配科学计数法
String regex = "\\d+\\.\\d+E[+-]?\\d+";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
List<String> sciNumbers = new ArrayList<>();
while (matcher.find()) {
sciNumbers.add(matcher.group());
}
System.out.println("原始文本: " + text);
System.out.println("提取到的科学计数法数字: " + sciNumbers); // 输出: [6.67430E-11]
}
}
进阶技巧与性能考量
使用预编译(Pattern.compile)提升性能
如果你的正则表达式在代码中被多次使用(在一个循环或高频调用的方法中),务必预编译它。Pattern.compile() 只执行一次,后续可以直接复用 Pattern 对象,避免了重复解析正则表达式字符串的开销。
错误示范(性能差):
for (int i = 0; i < 10000; i++) {
// 每次循环都重新编译,性能低下!
Matcher matcher = Pattern.compile("\\d+").matcher("some text 123");
// ...
}
正确示范(性能高):
// 在循环外编译一次
Pattern pattern = Pattern.compile("\\d+");
for (int i = 0; i < 10000; i++) {
Matcher matcher = pattern.matcher("some text 123");
// ...
}
使用命名分组(Java 8+)提升代码可读性
当正则表达式变得复杂时,使用数字索引(如 matcher.group(1))会降低代码可读性,Java 8 引入了命名分组,可以通过给分组起名字来访问。
语法:(?<name>pattern),访问时使用 matcher.group("name")。
示例:
import java.util.regex.*;
public class NamedGroupExample {
public static void main(String[] args) {
String text = "订单号:ORD-2025-10086";
// (?<orderType>\w+) 是一个命名分组,名为 orderType
String regex = "订单号:(?<orderType>\\w+)-(?<year>\\d+)-(?<id>\\d+)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
if (matcher.find()) {
// 使用名字访问,比 matcher.group(1) 更清晰
String orderType = matcher.group("orderType");
String year = matcher.group("year");
String id = matcher.group("id");
System.out.println("订单类型: " + orderType); // ORD
System.out.println("年份: " + year); // 2025
System.out.println("ID: " + id); // 10086
}
}
}
总结与最佳实践
本文系统地介绍了使用Java正则表达式提取数字的各种场景和技巧,作为程序员,请遵循以下最佳实践:
- 明确需求:在写正则之前,先想清楚你要匹配的数字格式边界在哪里。
- 从简到繁:先用简单的模式测试,逐步增加复杂性。
- 善用工具:利用在线正则表达式测试工具(如 Regex101)进行调试和验证。
- 预编译模式:在性能敏感的场景下,务必预编译你的正则表达式。
- 注重可读性:对于复杂正则,使用非贪婪量词(, )、注释(,Java不支持但其他引擎支持)和命名分组来保持代码清晰。
- 考虑边界:思考你的模式是否会误匹配。
\\d+会匹配a123b中的123,这是否是你想要的?如果只想匹配独立的数字,可以使用(?<!\\d)\\d+(?!\\d)(负向零宽断言)来确保前后没有其他数字字符。
掌握Java正则表达式提取数字,将极大提升你处理文本数据的效率和代码质量,希望本指南能成为你开发路上的得力助手!
(文章结束)
