從混合字符串(如 "abc123def456"、"訂單號:20230901")中提取連續數字子串,是日誌解析、數據清洗、表單驗證的高頻需求。本文匯總四種主流實現,附性能與適用場景分析。
public static List<String> extractNumbers(String input) {
List<String> result = new ArrayList<>();
int start = -1;
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (Character.isDigit(c)) {
if (start == -1) start = i; // 數字序列開始
} else {
if (start != -1) { // 數字序列結束
result.add(input.substring(start, i));
start = -1;
}
}
}
// 處理以數字結尾的情況
if (start != -1) result.add(input.substring(start));
return result;
}
優點:無依賴、可精確控制提取邏輯(如排除負號、小數點)、支持補充非 ASCII 數字(全角數字等);
缺點:代碼較長、需自行處理邊界。
public static List<String> extractNumbersRegex(String input) {
Pattern pattern = Pattern.compile("\\d+"); // 連續數字
Matcher matcher = pattern.matcher(input);
List<String> result = new ArrayList<>();
while (matcher.find()) {
result.add(matcher.group());
}
return result;
}
// 一行流式寫法 (Java 9+)
public static List<String> extractNumbersStream(String input) {
return Pattern.compile("\\d+").matcher(input).results()
.map(MatchResult::group).collect(Collectors.toList());
}
優點:代碼極少、可讀性強、易擴展(如 -?\\d+(\\.\\d+)? 提取負數/小數);
缺點:編譯 Pattern 有開銷(高頻調用建議靜態編譯緩存)、極長字符串可能回溯慢。
public static List<String> extractNumbersAscii(String input) {
List<String> result = new ArrayList<>();
int start = -1;
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (c >= '0' && c <= '9') { // ASCII 48-57
if (start == -1) start = i;
} else {
if (start != -1) {
result.add(input.substring(start, i));
start = -1;
}
}
}
if (start != -1) result.add(input.substring(start));
return result;
}
優點:無方法調用開銷、JIT 易優化、性能通常最優;
缺點:僅支持 ASCII 數字、可讀性稍差、硬編碼魔數。
// 原文片段:僅判斷「整串是否全為數字」,非提取
public static boolean isNumeric(String str) {
for (int i = str.length(); --i >= 0;) {
if (!Character.isDigit(str.charAt(i))) return false;
}
return true;
}
注意:此方法不能提取子串,只能驗證純數字字符串。若需提取,請用方法 1-3。
| 方法 | 吞吐量 | 適用場景 |
|---|---|---|
| ASCII 手動遍歷 | 最高 | 極高頻、極長文本、嵌入式/Android 早期版本 |
| Character.isDigit 遍歷 | 高(接近 ASCII) | 通用業務、需支持 Unicode 數字 |
| 正則 Pattern 復用 | 中高 | 業務邏輯複雜、需提取多種模式、代碼可讀性優先 |
| 正則臨時編譯 | 低 | 一次性腳本、低頻調用 |
Pattern NUMBER_PATTERN = Pattern.compile("\\d+"),復用 Matcher;-?\\d+(\\.\\d+)? 提取浮點數、0x[0-9a-fA-F]+ 提取十六進制);public final class NumberExtractor {
private static final Pattern DIGITS = Pattern.compile("\\d+");
private NumberExtractor() {}
/** 提取所有連續數字子串(推薦通用) */
public static List<String> extractAll(String input) {
if (input == null || input.isEmpty()) return Collections.emptyList();
Matcher m = DIGITS.matcher(input);
List<String> res = new ArrayList<>();
while (m.find()) res.add(m.group());
return res;
}
/** 提取第一個數字子串 */
public static String extractFirst(String input) {
if (input == null) return null;
Matcher m = DIGITS.matcher(input);
return m.find() ? m.group() : null;
}
/** 提取最後一個數字子串 */
public static String extractLast(String input) {
if (input == null) return null;
Matcher m = DIGITS.matcher(input);
String last = null;
while (m.find()) last = m.group();
return last;
}
/** 判斷純數字字符串 */
public static boolean isNumeric(String str) {
return str != null && DIGITS.matcher(str).matches(); // ^\\d+$
}
}
提取數字子串,正則表達式(靜態編譯復用)是首選,兼顧可讀性與性能;極致性能場景手寫 Character.isDigit() 或 ASCII 遍歷;切記區分「提取子串」與「驗證純數字」兩類不同需求,選對工具事半功倍。