在 Java 中獲取正則表達式模式匹配的索引
1. 概述
在 Java 編程中,處理字符串和模式對於許多應用程序至關重要。正則表達式(通常稱為 regex)為模式匹配和操作提供了強大的工具。
有時,我們不僅需要識別字符串中的匹配項,還需要準確定位這些匹配項發生的位置。在本教程中,我們將探索如何在 Java 中獲取正則表達式模式匹配的索引。
2.問題介紹
讓我們從一個String
示例開始:
String INPUT = "This line contains <the first value>, <the second value>, and <the third value>.";
假設我們要從上面的字符串中提取所有“<…>”
段,例如“ <the first value>
”和“ <the second value>
”。
為了匹配這些段,我們可以使用正則表達式的 NOR 字符類: “<[^>]*>”.
在 Java 中, Regex API 中的Pattern
和Matcher
類是處理模式匹配的重要工具。這些類提供了編譯正則表達式模式並將其應用於字符串以進行各種操作的方法。
接下來,讓我們使用Pattern
和Matcher
來提取所需的文本。為簡單起見,我們將使用 AssertJ 斷言來驗證是否獲得了預期結果:
Pattern pattern = Pattern.compile("<[^>]*>");
Matcher matcher = pattern.matcher(INPUT);
List<String> result = new ArrayList<>();
while (matcher.find()) {
result.add(matcher.group());
}
assertThat(result).containsExactly("<the first value>", "<the second value>", "<the third value>");
如上面的代碼所示,我們從輸入字符串中提取了所有“ <…>
”部分String.
然而,有時,我們想確切地知道匹配項在輸入中的位置。換句話說,我們想要獲取輸入字符串中的匹配項及其索引。
接下來,讓我們擴展這段代碼來實現我們的目標。
3. 獲取匹配索引
我們使用Matcher
類來提取匹配項。 Matcher
類提供了兩個方法, start()
和end(),
它們允許我們獲取每個匹配的開始和結束索引。
值得注意的是, Matcher.end()
方法返回匹配子序列的最後一個字符之後的索引。一個例子可以清楚地表明這一點:
Pattern pattern = Pattern.compile("456");
Matcher matcher = pattern.matcher("0123456789");
String result = null;
int startIdx = -1;
int endIdx = -1;
if (matcher.find()) {
result = matcher.group();
startIdx = matcher.start();
endIdx = matcher.end();
}
assertThat(result).isEqualTo("456");
assertThat(startIdx).isEqualTo(4);
assertThat(endIdx).isEqualTo(7); // matcher.end() returns 7 instead of 6
現在我們了解了start()
和end()
返回的內容,讓我們看看是否可以獲得 INPUT 中每個匹配的“<…>”
子序列的索引INPUT:
Pattern pattern = Pattern.compile("<[^>]*>");
Matcher matcher = pattern.matcher(INPUT);
List<String> result = new ArrayList<>();
Map<Integer, Integer> indexesOfMatches = new LinkedHashMap<>();
while (matcher.find()) {
result.add(matcher.group());
indexesOfMatches.put(matcher.start(), matcher.end());
}
assertThat(result).containsExactly("<the first value>", "<the second value>", "<the third value>");
assertThat(indexesOfMatches.entrySet()).map(entry -> INPUT.substring(entry.getKey(), entry.getValue()))
.containsExactly("<the first value>", "<the second value>", "<the third value>");
正如上面的測試所示,我們將每個匹配的start()
和end()
結果存儲在LinkedHashMap
中以保留插入順序。然後,我們通過這些索引對從原始輸入中提取子字符串。如果我們獲得了正確的索引,這些子字符串必須等於匹配項。
如果我們運行這個測試,它就會通過。
4. 獲取捕獲組的匹配索引
在正則表達式中,捕獲組發揮著至關重要的作用,它允許我們稍後引用它們或方便地提取子模式。
為了說明這一點,**假設我們的目標是提取“ <
”和“ >
”之間的內容。在這種情況下,我們可以創建一個包含捕獲組的模式: “<([^>]*)>”.
**因此,當使用Matcher.group(1),
我們獲得文本“ the first value
”、“ the second value
”,依此類推。
當未定義顯式捕獲組時,整個正則表達式模式採用索引為 0 的默認組。因此,調用Matcher.group()
與調用Matcher.group(0).
與Matcher.group()
函數的行為非常相似, Matcher.start()
和Matcher.end()
方法支持將組索引指定為參數。因此,這些方法提供了與相應組內的匹配內容相對應的開始和結束索引:
Pattern pattern = Pattern.compile("<([^>]*)>");
Matcher matcher = pattern.matcher(INPUT);
List<String> result = new ArrayList<>();
Map<Integer, Integer> indexesOfMatches = new LinkedHashMap<>();
while (matcher.find()) {
result.add(matcher.group(1));
indexesOfMatches.put(matcher.start(1), matcher.end(1));
}
assertThat(result).containsExactly("the first value", "the second value", "the third value");
assertThat(indexesOfMatches.entrySet()).map(entry -> INPUT.substring(entry.getKey(), entry.getValue()))
.containsExactly("the first value", "the second value", "the third value");
5. 結論
在本文中,我們探討了在處理正則表達式時獲取原始輸入中模式匹配的索引。我們討論了涉及帶有和不帶有明確定義的捕獲組的模式的場景。
與往常一樣,示例的完整源代碼可在 GitHub 上獲取。