Java 正则表达式
最后修改于 2024 年 1 月 27 日
Java 正则表达式教程展示了如何在 Java 中使用正则表达式解析文本。
正则表达式
正则表达式用于文本搜索和更高级的文本操作。正则表达式内置于包括 grep 和 Sed 在内的工具、包括 vi 和 emacs 在内的文本编辑器以及包括 Perl、Java 和 C# 在内的编程语言中。
Java 具有用于使用正则表达式的内置 API;它位于 java.util.regex
中。
正则表达式定义了字符串的搜索模式。Pattern
是正则表达式的编译表示。Matcher
是一个引擎,它解释该模式并针对输入字符串执行匹配操作。Matcher 具有诸如 find
、matches
、end
等方法来执行匹配操作。当解析正则表达式时出现异常时,Java 会抛出 PatternSyntaxException
。
Regex 示例
下表显示了一些正则表达式字符串。
Regex | 含义 |
---|---|
. |
匹配任何单个字符。 |
? |
匹配前一个元素一次或不匹配。 |
+ |
匹配前一个元素一次或多次。 |
* |
匹配前一个元素零次或多次。 |
^ |
匹配字符串内的起始位置。 |
$ |
匹配字符串内的结束位置。 |
| |
交替运算符。 |
[abc] |
匹配 a、b 或 c。 |
[a-c] |
范围;匹配 a、b 或 c。 |
[^abc] |
否定;匹配除 a、b 或 c 之外的所有字符。 |
\s |
匹配空白字符。 |
\w |
匹配单词字符;等同于 [a-zA-Z_0-9] |
Java 简单正则表达式
在第一个示例中,我们将一个单词与单词列表进行匹配。
package com.zetcode; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JavaRegexEx { public static void main(String[] args) { List<String> words = List.of("Seven", "even", "Maven", "Amen", "eleven"); Pattern p = Pattern.compile(".even"); for (String word: words) { Matcher m = p.matcher(word); if (m.matches()) { System.out.printf("%s matches%n", word); } else { System.out.printf("%s does not match%n", word); } } } }
在此示例中,我们在列表中有五个单词。 我们检查哪些单词与 .even
正则表达式匹配。
Pattern p = Pattern.compile(".even");
我们编译该模式。点 (.) 元字符代表文本中的任何单个字符。
for (String word: words) { Matcher m = p.matcher(word); if (m.matches()) { System.out.printf("%s matches%n", word); } else { System.out.printf("%s does not match%n", word); } }
我们遍历单词列表。使用 matcher
方法创建一个匹配器。如果单词与正则表达式匹配,则 matches
方法返回 true。
Seven matches even does not match Maven does not match Amen does not match eleven does not match
Java 正则表达式单词边界
元字符 \b
是一个锚点,它匹配一个称为单词边界的位置。它允许搜索整个单词。
package com.zetcode; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JavaRegexWordBoundaries { public static void main(String[] args) { var text = "This island is beautiful"; Pattern p = Pattern.compile("\\bis\\b"); Matcher matcher = p.matcher(text); while (matcher.find()) { System.out.printf("%s at %d", matcher.group(), matcher.start()); } } }
在该示例中,我们查找 is 单词。 我们不想包括 This 和 island 单词。
Pattern p = Pattern.compile("\\bis\\b");
使用两个 \b
元字符,我们搜索 is 整个单词。
while (matcher.find()) { System.out.printf("%s at %d", matcher.group(), matcher.start()); }
find
方法尝试查找与模式匹配的输入序列的下一个子序列。group
方法返回先前匹配项匹配的输入子序列。start
方法返回先前匹配项的起始索引。
is at 12
Java 正则表达式隐式单词边界
\w
是用于单词中允许的字符的字符类。对于表示单词的 \w+
正则表达式,前导和尾随单词边界元字符是隐式的;即 \w+
等于 \b\w+\b
。
package com.zetcode; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JavaRegexImplicitWordBoundaries { public static void main(String[] args) { var content = """ Foxes are omnivorous mammals belonging to several genera of the family Canidae. Foxes have a flattened skull, upright triangular ears, a pointed, slightly upturned snout, and a long bushy tail. Foxes live on every continent except Antarctica. By far the most common and widespread species of fox is the red fox."""; Pattern p = Pattern.compile("\\w+"); Matcher matcher = p.matcher(content); int count = 0; while (matcher.find()) { count++; System.out.println(matcher.group()); } System.out.printf("There are %d words", count); } }
在该示例中,我们搜索文本中的所有单词。
int count = 0; while (matcher.find()) { count++; System.out.println(matcher.group()); }
我们找到所有单词并对其进行计数。
Java 正则表达式货币符号
\p{Sc}
正则表达式可用于查找货币符号。
package com.zetcode; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JavaRegexCurrency { public static void main(String[] args) { String content = """ Currency symbols: ฿ Thailand bath, ₹ Indian rupee, ₾ Georgian lari, $ Dollar, € Euro, ¥ Yen, £ Pound Sterling"""; String regex = "\\p{Sc}"; Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(content); while (matcher.find()) { System.out.printf("%s at %d%n", matcher.group(), matcher.start()); } } }
在该示例中,我们查找货币符号。
String content = """ Currency symbols: ฿ Thailand bath, ₹ Indian rupee, ₾ Georgian lari, $ Dollar, € Euro, ¥ Yen, £ Pound Sterling""";
文本中有几个货币符号。
String regex = "\\p{Sc}";
我们定义货币符号的正则表达式。
while (matcher.find()) { System.out.printf("%s at %d%n", matcher.group(), matcher.start()); }
我们找到所有符号及其索引。
฿ at 18 ₹ at 35 ₾ at 51 $ at 68 € at 78 ¥ at 86 £ at 93
Java 正则表达式锚点
锚点匹配给定文本中字符的位置。在下一个示例中,我们查看一个字符串是否位于句子的开头。
package com.zetcode; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JavaRegexAnchor { public static void main(String[] args) { List<String> sentences = List.of("I am looking for Jane.", "Jane was walking along the river.", "Kate and Jane are close friends."); Pattern p = Pattern.compile("^Jane"); for (String word : sentences) { Matcher m = p.matcher(word); if (m.find()) { System.out.printf("%s matches%n", word); } else { System.out.printf("%s does not match%n", word); } } } }
我们有三个句子。搜索模式是 ^Jane
。该模式检查 "Jane" 字符串是否位于文本的开头。Jane\.$
将查找句子末尾的 "Jane"。
Java 正则表达式交替
交替运算符 | 使能够创建具有多个选项的正则表达式。
package com.zetcode; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JavaRegexAlternation { public static void main(String[] args) { List<String> users = List.of("Jane", "Thomas", "Robert", "Lucy", "Beky", "John", "Peter", "Andy"); Pattern p = Pattern.compile("Jane|Beky|Robert"); for (String user : users) { Matcher m = p.matcher(user); if (m.matches()) { System.out.printf("%s matches%n", user); } else { System.out.printf("%s does not match%n", user); } } } }
列表中有九个名字。
Pattern p = Pattern.compile("Jane|Beky|Robert");
这个正则表达式查找 "Jane"、"Beky" 或 "Robert" 字符串。
Java 正则表达式捕获组
圆括号 用于创建捕获组。 这使我们可以将量词应用于整个组,或者将交替选择限制为正则表达式的一部分。
package com.zetcode; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JavaRegexGroups { public static void main(String[] args) { var sites = List.of("webcode.me", "zetcode.com", "freebsd.org", "netbsd.org"); Pattern p = Pattern.compile("(\\w+)\\.(\\w+)"); for (var site: sites) { Matcher matcher = p.matcher(site); while (matcher.find()) { System.out.println(matcher.group(0)); System.out.println(matcher.group(1)); System.out.println(matcher.group(2)); } System.out.println("*****************"); } } }
在示例中,我们使用组将域名分成两部分。
Pattern p = Pattern.compile("(\\w+)\\.(\\w+)");
我们用括号定义了两个组。
while (matcher.find()) { System.out.println(matcher.group(0)); System.out.println(matcher.group(1)); System.out.println(matcher.group(2)); }
这些组通过 group
方法访问。group(0)
返回整个匹配的字符串。
webcode.me webcode me ***************** zetcode.com zetcode com ***************** freebsd.org freebsd org ***************** netbsd.org netbsd org *****************
在以下示例中,我们使用组来处理表达式。
package com.zetcode; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JavaRegexExpressions { public static void main(String[] args) { String[] expressions = {"16 + 11", "12 * 5", "27 / 3", "2 - 8"}; String pattern = "(\\d+)\\s+([-+*/])\\s+(\\d+)"; for (var expression : expressions) { Pattern p = Pattern.compile(pattern); Matcher matcher = p.matcher(expression); while (matcher.find()) { int val1 = Integer.parseInt(matcher.group(1)); int val2 = Integer.parseInt(matcher.group(3)); String oper = matcher.group(2); var result = switch (oper) { case "+" -> String.format("%s = %d", expression, val1 + val2); case "-" -> String.format("%s = %d", expression, val1 - val2); case "*" -> String.format("%s = %d", expression, val1 * val2); case "/" -> String.format("%s = %d", expression, val1 / val2); default -> "Unknown operator"; }; System.out.println(result); } } } }
该示例解析四个简单的数学表达式并对其进行计算。
String[] expressions = {"16 + 11", "12 * 5", "27 / 3", "2 - 8"};
我们有一个包含四个表达式的数组。
String pattern = "(\\d+)\\s+([-+*/])\\s+(\\d+)";
在正则表达式模式中,我们有三个组:两个组用于值,一个组用于运算符。
int val1 = Integer.parseInt(matcher.group(1)); int val2 = Integer.parseInt(matcher.group(3));
我们获取值并将其转换为整数。
String oper = matcher.group(2);
我们获取运算符。
var result = switch (oper) { case "+" -> String.format("%s = %d", expression, val1 + val2); case "-" -> String.format("%s = %d", expression, val1 - val2); case "*" -> String.format("%s = %d", expression, val1 * val2); case "/" -> String.format("%s = %d", expression, val1 / val2); default -> "Unknown operator"; };
使用 switch 表达式,我们计算表达式。
16 + 11 = 27 12 * 5 = 60 27 / 3 = 9 2 - 8 = -6
Java 正则表达式统计词频
在下一个示例中,我们统计文件中单词的频率。
$ wget https://raw.githubusercontent.com/janbodnar/data/main/the-king-james-bible.txt
我们使用《英王钦定本圣经》。
package com.zetcode; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.Map; import java.util.function.Function; import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; public class WordFreqEx { public static void main(String[] args) throws IOException { var fileName = "src/resources/the-king-james-bible.txt"; var text = Files.readString(Path.of(fileName)); var regex = "[a-zA-Z']+"; var p = Pattern.compile(regex); var matcher = p.matcher(text); var words = matcher.results(); var res = words.collect(Collectors.groupingBy(MatchResult::group, Collectors.counting())); res.entrySet().stream() .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) .limit(10) .forEach((e -> System.out.printf("%s %d%n", e.getKey(), e.getValue()))); } }
results
方法返回匹配结果的流。我们按单词出现的次数对单词进行分组。我们按频率对它们进行排序并打印前十个单词。
the 62103 and 38848 of 34478 to 13400 And 12846 that 12576 in 12331 shall 9760 he 9665 unto 8942
Java 正则表达式替换字符串
可以使用 replaceAll
和 replaceFirst
方法替换字符串。这些方法返回修改后的字符串。
package com.zetcode; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; public class JavaRegexReplacingStrings { public static void main(String[] args) throws IOException { URL url = new URL("http://webcode.me"); try (InputStreamReader isr = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(isr)) { String content = br.lines().collect( Collectors.joining(System.lineSeparator())); Pattern p = Pattern.compile("<[^>]*>"); Matcher matcher = p.matcher(content); String stripped = matcher.replaceAll(""); System.out.println(stripped); } } }
该示例读取网页的 HTML 数据并使用正则表达式剥离其 HTML 标签。
Pattern p = Pattern.compile("<[^>]*>");
此模式定义了一个匹配 HTML 标签的正则表达式。
String stripped = matcher.replaceAll("");
我们使用 replaceAll
方法删除所有标签。
Java 正则表达式拆分文本
可以使用 Pattern's
的 split
方法拆分文本。
22, 1, 3, 4, 5, 17, 18 2, 13, 4, 1, 8, 4 3, 21, 4, 5, 1, 48, 9, 42
我们从 data.csv
文件读取数据。
package com.zetcode; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.regex.Pattern; public class JavaRegexSplitText { static int sum = 0; public static void main(String[] args) throws IOException { Path myPath = Paths.get("src/resources/data.csv"); List<String> lines = Files.readAllLines(myPath); String regex = ","; Pattern p = Pattern.compile(regex); lines.forEach((line) -> { String[] parts = p.split(line); for (String part : parts) { String val = part.trim(); sum += Integer.parseInt(val); } }); System.out.printf("Sum of values: %d", sum); } }
该示例从 CSV 文件读取值并计算它们的总和。它使用正则表达式来读取数据。
List<String> lines = Files.readAllLines(myPath);
一次性地,我们使用 Files.readAllLines
将所有数据读取到字符串列表中。
String regex = ",";
正则表达式是一个逗号字符。
lines.forEach((line) -> { String[] parts = p.split(line); for (String part : parts) { String val = part.trim(); sum += Integer.parseInt(val); } });
我们遍历这些行,并使用 split
将它们拆分为字符串数组。我们用 trim
截断空格并计算总和值。
Java 不区分大小写的正则表达式
通过设置 Pattern.CASE_INSENSITIVE
标志,我们可以进行不区分大小写的匹配。
package com.zetcode; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JavaRegexCaseInsensitive { public static void main(String[] args) { List<String> users = List.of("dog", "Dog", "DOG", "Doggy"); Pattern p = Pattern.compile("dog", Pattern.CASE_INSENSITIVE); users.forEach((user) -> { Matcher m = p.matcher(user); if (m.matches()) { System.out.printf("%s matches%n", user); } else { System.out.printf("%s does not match%n", user); } }); } }
该示例执行正则表达式的不区分大小写的匹配。
Pattern p = Pattern.compile("dog", Pattern.CASE_INSENSITIVE);
通过将 Pattern.CASE_INSENSITIVE
设置为 Pattern.compile
的第二个参数来设置不区分大小写的匹配。
Java 正则表达式子模式
子模式是模式中的模式。 子模式使用 () 字符创建。
package com.zetcode; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JavaRegexSubpatterns { public static void main(String[] args) { List<String> words = Arrays.asList("book", "bookshelf", "bookworm", "bookcase", "bookish", "bookkeeper", "booklet", "bookmark"); Pattern p = Pattern.compile("book(worm|mark|keeper)?"); for (String word : words) { Matcher m = p.matcher(word); if (m.matches()) { System.out.printf("%s matches%n", word); } else { System.out.printf("%s does not match%n", word); } } } }
该示例创建一个子模式。
Pattern p = Pattern.compile("book(worm|mark|keeper)?");
正则表达式使用一个子模式。 它匹配 bookworm、bookmark、bookkeeper 和 book 单词。
Java 正则表达式电子邮件示例
在下面的示例中,我们创建一个 regex 模式来检查电子邮件地址。
package com.zetcode; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JavaRegexEmail { public static void main(String[] args) { List<String> emails = List.of("luke@gmail.com", "andy@yahoocom", "34234sdfa#2345", "f344@gmail.com"); String regex = "[a-zA-Z0-9._-]+@[a-zA-Z0-9-]+\\.[a-zA-Z.]{2,18}"; Pattern p = Pattern.compile(regex); for (String email : emails) { Matcher m = p.matcher(email); if (m.matches()) { System.out.printf("%s matches%n", email); } else { System.out.printf("%s does not match%n", email); } } } }
此示例仅提供一种可能的解决方案。
String regex = "[a-zA-Z0-9._-]+@[a-zA-Z0-9-]+\\.[a-zA-Z.]{2,18}";
电子邮件分为五个部分。第一部分是本地部分。这通常是公司、个人或昵称的名称。[a-zA-Z0-9._-]+
列出了所有可能的字符,我们可以在本地部分中使用。它们可以使用一次或多次。
第二部分由文字 @
字符组成。第三部分是域名部分。它通常是电子邮件提供商的域名,例如 yahoo 或 gmail。[a-zA-Z0-9-]+
是一个字符集,提供了可以在域名中使用的所有字符。+
量词使用这些字符中的一个或多个。
第四部分是点字符。它前面有转义字符 (\)。这是因为点字符是一个元字符,具有特殊的含义。通过转义它,我们得到一个文字点。
最后一部分是顶级域名:[a-zA-Z.]{2,18}
。顶级域名的长度可以从 2 到 18 个字符不等,例如 sk、net、info、travel、cleaning、travelinsurance。最大长度可以是 63 个字符,但如今大多数域名的长度都小于 18 个字符。还有一个点字符。这是因为一些顶级域名有两个部分;例如 co.uk。
来源
在本文中,我们使用了 Java 中的正则表达式。
作者
列出所有Java教程。