Java基础-正则表达式
Java Regex - Java Regular Expressions(Java正则表达式)
Java regex是官方的Java正则表达式API。术语Java regex是Java正则表达式的缩写。Java regex API位于Java .util中。regex包,自Java 1.4以来一直是标准Java (JSE)的一部分。本Java regex教程将解释如何使用这个API来匹配正则表达式和文本。
虽然Java regex从Java 1.4开始就是标准Java的一部分,但是本Java regex教程涵盖了Java 8发布的Java regex API。
Regular Expressions
正则表达式是用于在文本中搜索的文本模式。您可以通过将正则表达式与文本“匹配”来实现这一点。将正则表达式与文本匹配的结果是:
- 指定正则表达式是否匹配文本的true / false。
- 一组匹配项——文本中正则表达式的每个匹配项对应一个匹配项。
例如,您可以使用正则表达式搜索Java字符串,以查找电子邮件地址、url、电话号码、日期等。这可以通过对字符串匹配不同的正则表达式来实现。根据字符串匹配每个正则表达式的结果将是一组匹配—每个正则表达式对应一组匹配(每个正则表达式可能匹配不止一次)。
我将向您展示一些示例,演示如何使用下面的Java regex API将正则表达式与文本匹配。但是在下一节中,我将首先介绍Java regex API的核心类。
Java Regex Core Classes
Java regex API由两个核心类组成。这些都是:
Pattern类用于创建模式(正则表达式)。模式是以对象形式(作为模式实例)预编译的正则表达式,能够根据文本进行匹配。
Matcher类用于多次匹配给定正则表达式(模式实例)和文本。换句话说,要查找正则表达式在文本中的多次出现。Matcher将告诉您它在文本(字符索引)的哪个位置找到了这些事件。您可以从模式实例获取Matcher实例。
模式类和匹配器类都在各自的文本中详细介绍。请参阅上面的链接,或者在本Java regex教程的每个页面的左上角。
Java Regular Expression Example
如上所述,Java regex API可以告诉您正则表达式是否匹配某个字符串,或者返回字符串中该正则表达式的所有匹配。下面几节将向您展示使用Java regex API的这两种方法的示例。
Pattern Example
下面是一个简单的java正则表达式示例,它使用正则表达式检查文本是否包含子字符串http://:
String text =
"This is the text to be searched " +
"for occurrences of the http:// pattern.";
String regex = ".*http://.*";
boolean matches = Pattern.matches(regex, text);
System.out.println("matches = " + matches);
text变量包含要用正则表达式检查的文本。
pattern变量以字符串的形式包含正则表达式。正则表达式匹配所有文本,其中包含一个或多个字符(.),后跟文本http://,后跟一个或多个字符(.)。
第三行使用pattern .matches()静态方法检查正则表达式(pattern)是否匹配文本。如果正则表达式匹配文本,则Pattern.matches()返回true。如果正则表达式与文本模式不匹配,matches()返回false。
这个例子实际上并没有检查所找到的http://字符串是否是一个有效URL的一部分,该URL具有域名和后缀(.com、.net等)。正则表达式只检查字符串http://的出现情况。
Matcher Example
这是另一个Java regex例子,它使用Matcher类来定位文本中子字符串“is”的多次出现:
String text =
"This is the text which is to be searched " +
"for occurrences of the word 'is'.";
String regex = "is";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
int count = 0;
while(matcher.find()) {
count++;
System.out.println("found: " + count + " : "
+ matcher.start() + " - " + matcher.end());
}
从模式实例中获得一个Matcher实例。通过这个Matcher实例,示例可以找到文本中出现的所有正则表达式。
Java正则表达式语法
正则表达式的一个关键方面是正则表达式语法。Java不是唯一支持正则表达式的编程语言。大多数现代编程语言都支持正则表达式。不过,每种语言中定义正则表达式所用的语法并不完全相同。因此,您需要学习编程语言使用的语法。
在本Java regex教程的以下部分中,我将给出Java正则表达式语法的示例,以帮助您开始学习Java regex API和正则表达式。Java regex API使用的正则表达式语法将在关于Java正则表达式语法的文本中详细介绍
Matching Characters
首先要看的是如何编写与给定文本匹配字符的正则表达式。例如,这里定义的正则表达式:
String regex = "http://";
将匹配与正则表达式完全相同的所有字符串。http:// -前后不能有字符,否则正则表达式将与文本不匹配。例如,上面的正则表达式将匹配以下文本:
String text1 = "http://";
但不是这段文字:
String text2 = "The URL is: http://mydomain.com";
第二个字符串包含与之匹配的http://之前和之后的字符。
Metacharacters
元字符是正则表达式中的字符,被解释为具有特殊含义。这些元字符是:
Character | Description |
---|---|
< | |
> | |
( | |
) | |
[ | |
] | |
{ | |
} | |
\ | |
^ | |
- | |
= | |
$ | |
! | |
| | |
? | |
* | |
+ | |
. |
这些元字符的确切含义将在Java Regex教程中进一步解释。只要记住,如果你包括了一个“”。(fullstop)在正则表达式中,它将不匹配一个fullstop字符,而是匹配由该元字符定义的其他内容(稍后也会解释)。
Escaping Characters转义字符
如上所述,Java正则表达式中的元字符具有特殊的意义。如果您真的想匹配这些字符的文字形式,而不是它们的元字符含义,那么您必须“转义”您想匹配的元字符。要转义元字符,可以使用Java正则表达式转义字符——反斜杠字符。转义字符意味着在字符前面加上反斜杠字符。例如,像这样:
\.
在这个例子中。字符的前面(转义)是\字符。转义后,fullstop字符将实际匹配输入文本中的fullstop字符。转义元字符的特殊元字符含义被忽略——只使用它的实际文字值(例如句号)。
Java正则表达式语法使用反斜杠字符作为转义字符,就像Java字符串一样。在用Java字符串编写正则表达式时,这会带来一点挑战。看看这个正则表达式示例:
String regex = "\\.";
注意正则表达式字符串包含两个反斜杠,然后是。原因是,首先Java编译器将这两个\字符解释为转义的Java字符串字符。Java编译器完成后,只剩下一个\,因为\表示字符\。字符串是这样的:
\.
现在Java正则表达式解释器开始工作,并将剩余的反斜杠解释为转义字符。下面的字符。现在被解释为实际的句号,而不是具有特殊的正则表达式。因此,剩下的正则表达式只匹配句号字符。
在Java正则表达式语法中,有几个字符具有特殊的含义。如果希望匹配该显式字符而不使用其特殊含义,则需要首先使用反斜杠转义它。例如,要匹配句号字符,您需要编写:
String regex = "\\.";
要匹配反斜杠字符本身,您需要编写:
String regex = "\\\\";
在正则表达式中正确地转义字符可能比较棘手。对于高级正则表达式,您可能需要花一段时间才能正确使用它。
Matching Any Character匹配任何字符
到目前为止,我们只看到了如何匹配特定的字符,如“h”、“t”、“p”等。但是,您也可以匹配任何字符,而不考虑它是什么字符。Java正则表达式语法允许您使用。字符(句号/句号)。下面是一个匹配任何字符的正则表达式示例:
String regex = ".";
这个正则表达式匹配一个字符,无论它是什么字符。
字符可以与其他字符组合以创建更高级的正则表达式。举个例子:
String regex = "H.llo";
这个正则表达式将匹配任何Java字符串,该字符串包含字符“H”后跟任意字符,后跟字符“llo”。因此,这个正则表达式将匹配所有字符串“Hello”、“Hallo”、“Hullo”、“Hxllo”等。
Matching Any of a Set of Characters匹配一组字符中的任何一个
Java正则表达式支持使用所谓的字符类匹配任何指定的字符集。下面是一个字符类的例子:
String regex = "H[ae]llo";
字符类(要匹配的字符集)被括在方括号中——换句话说,就是正则表达式的[ae]部分。方括号不匹配—只匹配其中的字符。
字符类将匹配所包含的任何一个字符,但不能超过一个。因此,上面的正则表达式将匹配两个字符串中的任何一个“Hallo”或“Hello”,但不匹配其他字符串。在“H”和“llo”之间只允许有“a”或“e”。
您可以通过在范围内指定第一个和最后一个字符,并在它们之间加上一个破折号来匹配一个字符范围。例如,字符类[a-z]将匹配小写a和小写z之间的所有字符,包括a和z。
在一个字符类中可以有多个字符范围。例如,字符类[a- za -z]将匹配a和z之间或a和z之间的所有字母。
您还可以对数字使用范围。例如,character类[0-9]将匹配包含在0和9之间的字符。
如果您想实际匹配文本中的一个方括号,则需要转义它们。下面是如何转义方括号:
String regex = "H\\[llo";
\[是转义的左方括号。这个正则表达式将匹配字符串“H[llo"。
如果你想匹配字符类中的方括号,如下所示:
String regex = "H[\\[\\]]llo";
字符类是这一部分:[\[\]]。character类包含转义的两个方括号(\[和\])。
这个正则表达式将匹配字符串“H[llo]”和“H]llo”。
Matching a Range of Characters匹配一系列字符
Java regex API允许您指定要匹配的字符范围。指定字符范围比显式指定要匹配的每个字符要容易。例如,你可以像这样匹配字符a和z:
String regex = "[a-z]";
这个正则表达式将匹配字母表中从a到z的任何单个字符。
字符类是区分大小写的。要匹配从a到z的所有字符(无论大小写),必须同时包含大写和小写字符范围。它是这样的:
String regex = "[a-zA-Z]";
Matching Digits匹配数字
您可以使用代码\d将数字与预定义的字符类匹配。数字字符类对应于字符类[0-9]。
因为\字符在Java中也是转义字符,所以需要在Java字符串中使用两个反斜杠才能在正则表达式中得到\d。下面是这样一个正则表达式字符串的样子:
String regex = "Hi\\d";
这个正则表达式将匹配以“Hi”开头,后跟数字(0到9)的字符串。因此,它将匹配字符串“Hi5”,但不匹配字符串“Hip”。
Matching Non-digits匹配非数字字符
可使用预定义的字符类\D来匹配非数字。以下是一个包含非数字字符类的正则表达式:
String regex = "Hi\\D";
这个正则表达式将匹配任何以“Hi”开头,后面跟着一个字符(不是数字)的字符串。
Matching Word Characters匹配单词字符
您可以使用代码\w将word字符与预定义的字符类匹配。word字符类对应于字符类[a-zA-Z_0-9]。
String regex = "Hi\\w";
这个正则表达式将匹配任何以“Hi”开头,后跟单个单词字符的字符串。
Matching Non-word Characters匹配非单词字符
您可以将非单词字符与预定义的字符类\W匹配。由于\字符在Java中也是转义字符,因此需要在Java字符串中使用两个反斜杠才能在正则表达式中得到\W。下面是这样一个正则表达式字符串的样子:
下面是一个使用非单词字符类的正则表达式示例:
String regex = "Hi\\W";
Boundaries边界
Java Regex API还可以匹配字符串中的边界。边界可以是字符串的开头、字符串的结尾、单词的开头等等。Java Regex API支持以下边界:
输入结束
符号 | 描述 |
---|---|
^ | 一行的开头。 |
$ | 一行的末尾。 |
\b | 单词边界(单词开始或结束的地方,如空格、制表符等)。 |
\B | 一个非单词边界。 |
\A | 输入的开始。 |
\G | 前一场匹配的结束。 |
\Z | 输入结束符,但用于最终终止符(如果有)。 |
\z |
下面将解释其中一些边界匹配器。
Beginning of Line (or String)行(或字符串)的开头
^边界匹配器根据Java API规范匹配行首。然而,实际上它似乎只匹配字符串的开头。例如,下面的例子只在索引0处得到一个匹配项:
String text = "Line 1\nLine2\nLine3";
Pattern pattern = Pattern.compile("^");
Matcher matcher = pattern.matcher(text);
while(matcher.find()){
System.out.println("Found match at: " + matcher.start() + " to " + matcher.end());
}
即使输入字符串包含多个换行符,^字符也只匹配输入字符串的开头,而不匹配每行(换行后)的开头。
行/字符串匹配器的开头通常与其他字符结合使用,以检查字符串是否以某个子字符串开头。例如,这个例子检查输入字符串是否以子字符串http://开头:
String text = "http://jenkov.com";
Pattern pattern = Pattern.compile("^http://");
Matcher matcher = pattern.matcher(text);
while(matcher.find()){
System.out.println("Found match at: " + matcher.start() + " to " + matcher.end());
}
本例在输入流中找到索引0到索引7之间的子字符串http://的一个匹配项。即使输入字符串包含更多的子字符串http://实例,它们也不会被这个正则表达式匹配,因为正则表达式以^字符开始。
End of Line (or String)行尾(或字符串)
$ boundary matcher根据Java规范匹配行尾。然而,实际上,它看起来只匹配输入字符串的末尾。
行(或字符串)匹配器的开头通常与其他字符结合使用,最常用来检查字符串是否以某个子字符串结束。下面是一个结束行/字符串匹配器的例子:
String text = "http://jenkov.com";
Pattern pattern = Pattern.compile(".com$");
Matcher matcher = pattern.matcher(text);
while(matcher.find()){
System.out.println("Found match at: " + matcher.start() + " to " + matcher.end());
}
本例将在输入字符串的末尾找到一个匹配项。
Word Boundaries单词边界
边界匹配器匹配单词边界,即输入字符串中单词开始或结束的位置。
下面是一个Java正则表达式单词边界的例子:
String text = "Mary had a little lamb";
Pattern pattern = Pattern.compile("\\b");
Matcher matcher = pattern.matcher(text);
while(matcher.find()){
System.out.println("Found match at: " + matcher.start() + " to " + matcher.end());
}
此示例匹配在输入字符串中找到的所有单词边界。注意单词边界匹配器是如何用\b -和两个\(反斜杠)字符编写的。原因将在关于转义字符的部分中解释。Java编译器使用\作为转义字符,因此需要两个反斜杠字符,以便在字符串中插入一个反斜杠字符。
运行这个例子的输出将是:
Found match at: 0 to 0
Found match at: 4 to 4
Found match at: 5 to 5
Found match at: 8 to 8
Found match at: 9 to 9
Found match at: 10 to 10
Found match at: 11 to 11
Found match at: 17 to 17
Found match at: 18 to 18
Found match at: 22 to 22
输出列出了单词在输入字符串中开始或结束的所有位置。可以看到,单词开头的索引指向单词的第一个字符,而单词的结尾指向单词后面的第一个字符。
您可以将单词边界匹配器与其他字符组合使用,以搜索以特定字符开头的单词。举个例子:
String text = "Mary had a little lamb";
Pattern pattern = Pattern.compile("\\bl");
Matcher matcher = pattern.matcher(text);
while(matcher.find()){
System.out.println("Found match at: " + matcher.start() + " to " + matcher.end());
}
本例将查找单词以字母l(小写)开头的所有位置。实际上,它还将找到这些匹配的结尾,即模式的最后一个字符,即小写的l字母。
Non-word Boundaries
边界匹配器匹配非单词边界。非词界是同一词的两个字符之间的边界。换句话说,字符组合不是单词到非单词的字符序列(这是一个单词边界)。下面是一个简单的Java regex非单词边界匹配器示例:
String text = "Mary had a little lamb";
Pattern pattern = Pattern.compile("\\B");
Matcher matcher = pattern.matcher(text);
while(matcher.find()){
System.out.println("Found match at: " + matcher.start() + " to " + matcher.end());
}
这个例子将给出以下输出:
Found match at: 1 to 1
Found match at: 2 to 2
Found match at: 3 to 3
Found match at: 6 to 6
Found match at: 7 to 7
Found match at: 12 to 12
Found match at: 13 to 13
Found match at: 14 to 14
Found match at: 15 to 15
Found match at: 16 to 16
Found match at: 19 to 19
Found match at: 20 to 20
Found match at: 21 to 21
请注意这些匹配索引如何对应同一单词内字符之间的边界。
Quantifiers
量词可以多次用于匹配字符。在Java Regex语法中列出了几种量词。我将在这里介绍一些最常用的量词。
前两个量词是*和+字符。将其中一个字符放在要匹配的字符后面多次。下面是一个带量词的正则表达式:
String regex = "Hello*";
此正则表达式将字符串与后跟0或多个o字符的文本“Hell”匹配。因此,正则表达式将匹配“Hell”、“Hello”、“Helloo”等。
如果量词是+字符而不是*字符,那么字符串必须以1个或多个o字符结束。
如果您想匹配这两个量词字符中的任何一个,您将需要转义它们。下面是一个转义+量词的例子:
String regex = "Hell\\+";
这个正则表达式将匹配字符串“Hell+”;
还可以使用{n}量词匹配特定字符的确切数量,其中n是要匹配的字符数量。举个例子:
String regex = "Hello{2}";
这个正则表达式将匹配字符串“Helloo”(末尾有两个o字符)。
您可以设置您想要匹配的字符数量的上限和下限,如下所示:
String regex = "Hello{2,4}";
这个正则表达式将匹配字符串“Helloo”、“Hellooo”和“Helloooo”。换句话说,字符串“Hell”末尾有2、3或4个o字符。
Logical Operators
Java Regex API支持一组逻辑操作符,这些逻辑操作符可用于在一个正则表达式中组合多个子模式。Java Regex API支持两个逻辑操作符:and操作符和or操作符。
and运算符是隐式的。如果正则表达式中的两个字符(或其他子模式)彼此遵循,这意味着第一个和第二个子模式都与目标字符串匹配得很好。下面是一个使用隐式和运算符的正则表达式的例子:
String text = "Cindarella and Sleeping Beauty sat in a tree";
Pattern pattern = Pattern.compile("[Cc][Ii].*");
Matcher matcher = pattern.matcher(text);
System.out.println("matcher.matches() = " + matcher.matches());
注意这3个子模式[Cc]、[Ii]和.*
由于正则表达式中这些子模式之间没有字符,因此它们之间隐含着and运算符。这意味着,目标字符串必须按照给定的顺序匹配所有3个子模式,以匹配正则表达式作为一个整体。从字符串中可以看到,表达式与字符串匹配。字符串应该以大写或小写C开头,然后是大写或小写I,然后是零或多个字符。字符串满足这些条件。
or操作符是显式的,由管道字符|表示。下面是一个正则表达式的例子,它包含两个子表达式,其中逻辑或运算符位于两者之间:
String text = "Cindarella and Sleeping Beauty sat in a tree";
Pattern pattern = Pattern.compile(".*Ariel.*|.*Sleeping Beauty.*");
Matcher matcher = pattern.matcher(text);
System.out.println("matcher.matches() = " + matcher.matches());
如您所见,该模式将匹配目标字符串中的子模式Ariel或子模式睡美人。因为目标字符串包含文本睡美人,所以正则表达式匹配目标字符串。
Java String Regex Methods(Java字符串正则方法)
Java String类也有一些正则表达式方法。我将在这里介绍其中一些:
matches()
方法将正则表达式作为参数,如果正则表达式匹配字符串,则返回true,如果不匹配,则返回false。
下面是一个matches()例子:
String text = "one two three two one";
boolean matches = text.matches(".*two.*");
split()
方法将字符串分割成N个子字符串并返回一个包含这些子字符串的字符串数组。split()方法接受一个正则表达式作为参数,并在正则表达式与字符串的一部分匹配的所有位置上分割字符串。正则表达式不作为返回子字符串的一部分返回。
下面是一个split()例子:
String text = "one two three two one";
String[] twos = text.split("two");
这个例子将返回三个字符串“1”、“3”和“1”。
replaceFirst()
方法的作用是:返回一个新字符串,其中包含作为第一个参数传递的正则表达式的第一个匹配项,以及第二个参数的字符串值。
下面是一个replaceFirst()例子:
String text = "one two three two one";
String s = text.replaceFirst("two", "five");
这个例子将返回字符串“one five three two one”。
replaceAll()
方法的作用是:返回一个新字符串,其中包含作为第一个参数传递的正则表达式的所有匹配项,以及第二个参数的字符串值。
下面是replaceAll()的例子:
String text = "one two three two one";
String t = text.replaceAll("two", "five");
这个例子将返回字符串“one five three five one”。
Pattern类
Java的Pattern类(Java .util.regex.Pattern)是Java正则表达式API的主要访问点。无论何时需要使用Java中的正则表达式,都可以从Java的Pattern类开始。
在Java中使用正则表达式有时也称为模式匹配。正则表达式有时也称为模式(因此是Java模式类的名称)。因此,Java中的术语模式匹配意味着使用Java将正则表达式(模式)与文本匹配。
Java模式类可以以两种方式使用。可以使用Pattern.matches()方法快速检查文本(字符串)是否匹配给定的正则表达式。或者可以使用Pattern.compile()编译一个模式实例,可以多次使用该实例来匹配正则表达式和多个文本。下面将介绍Pattern.matches()和Pattern.compile()方法。
Pattern.matches()
检查正则表达式模式是否匹配文本的最简单方法是使用静态pattern .matches()方法。下面是Java代码中的一个Pattern.matches()示例:
import java.util.regex.Pattern;
public class PatternMatchesExample {
public static void main(String[] args) {
String text =
"This is the text to be searched " +
"for occurrences of the pattern.";
String pattern = ".*is.*";
boolean matches = Pattern.matches(pattern, text);
System.out.println("matches = " + matches);
}
}
matches()示例搜索文本变量引用的字符串,查找单词“is”的出现情况,允许在单词之前和之后出现零个或多个字符(模式的两个.*部分)。
如果您只需要一次针对文本检查模式,那么pattern .matches()方法就很好,而且pattern类的默认设置也很合适。
如果需要匹配多个匹配项,甚至访问各种匹配项,或者只需要非默认设置,则需要使用Pattern.compile()方法编译模式实例。
Pattern.compile()
如果需要多次将文本与正则表达式模式匹配,则需要使用pattern .compile()方法创建模式实例。下面是一个Java Pattern.compile()示例:
import java.util.regex.Pattern;
public class PatternCompileExample {
public static void main(String[] args) {
String text =
"This is the text to be searched " +
"for occurrences of the http:// pattern.";
String patternString = ".*http://.*";
Pattern pattern = Pattern.compile(patternString);
}
}
还可以使用Pattern.compile()方法使用特殊标志编译模式。下面是一个使用特殊标志的Java Pattern.compile()示例:
Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
Java Pattern类包含一系列标志(int常量),您可以使用这些标志使模式匹配以特定的方式工作。上面使用的标志使模式匹配在匹配时忽略文本的大小写。有关可以与Java模式类一起使用的标志的更多信息,请参见JavaDoc中的模式。
Pattern.matcher()
一旦获得了模式实例,就可以使用它来获得Matcher实例。Matcher实例用于在文本中查找模式的匹配项。下面是一个如何从模式实例创建Matcher实例的例子:
Matcher matcher = pattern.matcher(text);
Matcher类有一个matches()方法,用于测试模式是否匹配文本。下面是如何使用匹配器的完整示例:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class PatternMatcherExample {
public static void main(String[] args) {
String text =
"This is the text to be searched " +
"for occurrences of the http:// pattern.";
String patternString = ".*http://.*";
Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(text);
boolean matches = matcher.matches();
System.out.println("matches = " + matches);
}
}
Matcher非常高级,允许您以多种方式访问文本的匹配部分。本文也要保持简短,Matcher在关于Java Matcher类的文章中有更详细的介绍。
Pattern.split()
Pattern类中的split()方法可以使用正则表达式(模式)作为分隔符,将文本分割为字符串数组。下面是一个Java模式.split()示例:
import java.util.regex.Pattern;
public class PatternSplitExample {
public static void main(String[] args) {
String text = "A sep Text sep With sep Many sep Separators";
String patternString = "sep";
Pattern pattern = Pattern.compile(patternString);
String[] split = pattern.split(text);
System.out.println("split.length = " + split.length);
for(String element : split){
System.out.println("element = " + element);
}
}
}
split()示例将文本变量中的文本分割为5个单独的字符串。这些字符串都包含在split()方法返回的字符串数组中。与分隔符匹配的文本部分不包含在返回的字符串数组中。
Pattern.pattern()
pattern类的pattern()方法只返回模式实例编译时使用的模式字符串(正则表达式)。举个例子:
import java.util.regex.Pattern;
public class PatternPatternExample {
public static void main(String[] args) {
String patternString = "sep";
Pattern pattern = Pattern.compile(patternString);
String pattern2 = pattern.pattern();
}
}
在本例中,pattern2变量将包含值sep,这是模式实例编译的值。
Matcher类
Java Matcher类(Java .util. regx .Matcher)用于在文本中搜索正则表达式的多次出现。您还可以使用匹配器在不同的文本中搜索相同的正则表达式。
Java Matcher类有很多有用的方法。在本教程中,我将介绍Java Matcher类的核心方法。有关完整列表,请参阅Matcher类的官方JavaDoc。
Java Matcher Example
下面是一个快速的Java匹配器示例,您可以了解匹配器类是如何工作的:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class MatcherExample {
public static void main(String[] args) {
String text =
"This is the text to be searched " +
"for occurrences of the http:// pattern.";
String patternString = ".*http://.*";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(text);
boolean matches = matcher.matches();
}
}
首先,从正则表达式创建模式实例,然后从模式实例创建Matcher实例。然后在Matcher实例上调用matches()方法。如果正则表达式与文本匹配,matches()返回true,如果不匹配,返回false。
您可以使用Matcher类做更多的事情。其余部分将在本教程的其余部分中介绍。模式类在我的Java Regex模式教程中单独介绍。
Creating a Matcher
Creating a Matcher
is done via the matcher()
method in the Pattern
class. Here is an example:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class CreateMatcherExample {
public static void main(String[] args) {
String text =
"This is the text to be searched " +
"for occurrences of the http:// pattern.";
String patternString = ".*http://.*";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(text);
}
}
在本例的末尾,matcher变量将包含一个matcher实例,该实例可用于匹配用于针对不同文本输入创建它的正则表达式。
matches()
Matcher类中的matches()方法将正则表达式与创建Matcher时传递给Pattern.matcher()方法的整个文本进行匹配。下面是一个Matcher.matches()例子:
String patternString = ".*http://.*";
Pattern pattern = Pattern.compile(patternString);
boolean matches = matcher.matches();
如果正则表达式匹配整个文本,则matches()方法返回true。如果没有,matches()方法返回false。
不能使用matches()方法搜索文本中正则表达式的多次出现。为此,您需要使用find()、start()和end()方法。
lookingAt()
Matcher lookingAt()方法的工作原理与matches()方法类似,但有一个主要区别。lookingAt()方法只匹配文本开头的正则表达式,而matches()匹配整个文本的正则表达式。换句话说,如果正则表达式匹配文本的开头而不是整个文本,那么lookingAt()将返回true,而matches()将返回false。
下面是一个matcher .look()例子:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class CreateMatcherExample {
public static void main(String[] args) {
String text =
"This is the text to be searched " +
"for occurrences of the http:// pattern.";
String patternString = "This is the";
Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(text);
System.out.println("lookingAt = " + matcher.lookingAt());
System.out.println("matches = " + matcher.matches());
}
}
这个例子将正则表达式“This is the”匹配到文本的开头和整个文本。将正则表达式与文本开头匹配(look kingat())将返回true。
将正则表达式与整个文本匹配(matches())将返回false,因为文本的字符比正则表达式多。正则表达式表示文本必须与文本“This is The”完全匹配,在表达式之前或之后没有额外的字符。
find() + start() + end()
Matcher find()方法搜索在创建Matcher时传递给Pattern.matcher(text)方法的文本中正则表达式的出现情况。如果在文本中可以找到多个匹配项,find()方法将找到第一个匹配项,然后对于每个后续的find()调用,它将移动到下一个匹配项。
方法start()和end()将在找到匹配的开始和结束位置的文本中提供索引。实际上end()返回匹配部分结束后字符的索引。因此,可以在字符串中使用start()和end()的返回值。
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class MatcherFindStartEndExample {
public static void main(String[] args) {
String text =
"This is the text which is to be searched " +
"for occurrences of the word 'is'.";
String patternString = "is";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(text);
int count = 0;
while(matcher.find()) {
count++;
System.out.println("found: " + count + " : "
+ matcher.start() + " - " + matcher.end());
}
}
}
本例将在搜索的字符串中发现模式“is”四次。打印的输出如下:
found: 1 : 2 - 4
found: 2 : 5 - 7
found: 3 : 23 - 25
found: 4 : 70 - 72
reset()
方法的作用是:在匹配器内部重置匹配状态。如果您已经通过find()方法开始匹配字符串中的匹配事件,Matcher将在内部保存关于它在输入文本中搜索了多远的状态。通过调用reset(),匹配将再次从文本的开头开始。
还有一个重置(CharSequence)方法。该方法重置匹配器,并使匹配器搜索作为参数传递的CharSequence,而不是最初创建匹配器时使用的CharSequence。
group()
假设您在文本中搜索URL,并希望从文本中提取找到的URL。当然,您可以使用start()和end()方法来实现这一点,但是使用组函数更容易。
正则表达式中使用括号标记组。例如:
(John)
这个正则表达式与文本John匹配。括号不是匹配文本的一部分。括号表示一组。当在文本中找到匹配项时,可以访问组中正则表达式的部分。
使用group(int groupNo)方法访问一个组。正则表达式可以有多个组。因此,每个组都用一组单独的括号标记。要访问与特定组中表达式的子部分匹配的文本,请将组号传递给group(int groupNo)方法。
数字为0的组总是整个正则表达式。要访问用括号标记的组,应该从组号1开始。
下面是Matcher group()的例子:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class MatcherGroupExample {
public static void main(String[] args) {
String text =
"John writes about this, and John writes about that," +
" and John writes about everything. "
;
String patternString1 = "(John)";
Pattern pattern = Pattern.compile(patternString1);
Matcher matcher = pattern.matcher(text);
while(matcher.find()) {
System.out.println("found: " + matcher.group(1));
}
}
}
这个例子搜索文本中出现的单词John。对于找到的每个匹配项,提取组1,它与用括号标记的组匹配。例子的输出为:
found: John
found: John
found: John
Multiple Groups
如前所述,正则表达式可以有多个组。下面是一个正则表达式:
(John) (.+?)
这个表达式匹配文本“John”,后跟空格,然后是一个或多个字符。您在上面的示例中看不到它,但是在最后一个组后面也有一个空格。
这个表达式在正则表达式中包含一些具有特殊含义的字符。的。意思是“任何字符”。+表示“一次或多次”,与(任何字符,一次或多次)。的吗?意思是“匹配尽可能少的字符”。
下面是一个完整的代码示例:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class MatcherGroupExample {
public static void main(String[] args) {
String text =
"John writes about this, and John Doe writes about that," +
" and John Wayne writes about everything."
;
String patternString1 = "(John) (.+?) ";
Pattern pattern = Pattern.compile(patternString1);
Matcher matcher = pattern.matcher(text);
while(matcher.find()) {
System.out.println("found: " + matcher.group(1) +
" " + matcher.group(2));
}
}
}
请注意对两个组的引用,用粗体标记。这些组匹配的字符被打印到System.out。下面是打印出来的例子:
found: John writes
found: John Doe
found: John Wayne
Groups Inside Groups
在正则表达式中可以在组中包含组。举个例子:
((John) (.+?))
注意前面示例中的两个组现在如何嵌套在一个更大的组中。(同样,您不能看到表达式末尾的空格,但是它在那里)。
当组彼此嵌套在一起时,将根据组的左对位何时满足对它们进行编号。因此,第一组是大组。第二组是包含表达式John的组。第三组是带表达式的组。在里面。知道何时需要通过groups(int groupNo)方法引用组是很重要的。
下面是一个使用上述嵌套组的例子:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class MatcherGroupsExample {
public static void main(String[] args) {
String text =
"John writes about this, and John Doe writes about that," +
" and John Wayne writes about everything."
;
String patternString1 = "((John) (.+?)) ";
Pattern pattern = Pattern.compile(patternString1);
Matcher matcher = pattern.matcher(text);
while(matcher.find()) {
System.out.println("found: <" + matcher.group(1) +
"> <" + matcher.group(2) +
"> <" + matcher.group(3) + ">");
}
}
}
下面是上面例子的输出:
found: <John writes> <John> <writes>
found: <John Doe> <John> <Doe>
found: <John Wayne> <John> <Wayne>
注意,第一个组(外部组)匹配的值包含两个内部组匹配的值。
replaceAll() + replaceFirst()
Matcher replaceAll()和replaceFirst()方法可用于替换Matcher正在搜索的字符串的部分。方法的作用是:替换正则表达式的所有匹配项。replaceFirst()只替换第一个匹配项。
在执行任何匹配之前,将重置匹配器,以便从输入文本的开头开始匹配。
这里有两个例子:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class MatcherReplaceExample {
public static void main(String[] args) {
String text =
"John writes about this, and John Doe writes about that," +
" and John Wayne writes about everything."
;
String patternString1 = "((John) (.+?)) ";
Pattern pattern = Pattern.compile(patternString1);
Matcher matcher = pattern.matcher(text);
String replaceAll = matcher.replaceAll("Joe Blocks ");
System.out.println("replaceAll = " + replaceAll);
String replaceFirst = matcher.replaceFirst("Joe Blocks ");
System.out.println("replaceFirst = " + replaceFirst);
}
}
下面是例子的输出:
replaceAll = Joe Blocks about this, and Joe Blocks writes about that,
and Joe Blocks writes about everything.
replaceFirst = Joe Blocks about this, and John Doe writes about that,
and John Wayne writes about everything.
下面一行的换行和缩进实际上不是输出的一部分。我添加它们是为了使输出更容易阅读。
注意,第一个字符串是如何打印的,它将所有出现的John和一个单词after替换为string Joe Blocks。第二个字符串只替换了第一个匹配项。
appendReplacement() + appendTail()
Matcher appendreplace()和appendTail()方法用于替换输入文本中的字符串标记,并将结果字符串附加到StringBuffer中。
当您使用find()方法找到匹配项时,可以调用appendReplacement()。这样做会将输入文本中的字符追加到StringBuffer,并替换匹配的文本。只有字符从最后一个匹配的末尾开始,直到匹配的字符被复制之前。
appendreplace()方法跟踪复制到StringBuffer中的内容,因此可以继续使用find()搜索匹配项,直到在输入文本中不再找到匹配项为止。
一旦找到最后一个匹配项,输入文本的一部分仍然没有复制到StringBuffer中。这是从上次匹配结束到输入文本结束的字符。通过调用appendTail(),您也可以将这些最后的字符追加到StringBuffer。
举个例子:
mport java.util.regex.Pattern;
import java.util.regex.Matcher;
public class MatcherReplaceExample {
public static void main(String[] args) {
String text =
"John writes about this, and John Doe writes about that," +
" and John Wayne writes about everything."
;
String patternString1 = "((John) (.+?)) ";
Pattern pattern = Pattern.compile(patternString1);
Matcher matcher = pattern.matcher(text);
StringBuffer stringBuffer = new StringBuffer();
while(matcher.find()){
matcher.appendReplacement(stringBuffer, "Joe Blocks ");
System.out.println(stringBuffer.toString());
}
matcher.appendTail(stringBuffer);
System.out.println(stringBuffer.toString());
}
}
注意,如何在while(mat .find())循环中调用appendReplacement(),以及在循环之后调用appendTail()。
这个例子的输出是:
Joe Blocks
Joe Blocks about this, and Joe Blocks
Joe Blocks about this, and Joe Blocks writes about that, and Joe Blocks
Joe Blocks about this, and Joe Blocks writes about that, and Joe Blocks
writes about everything.
我插入了最后一行中的换行符,使文本更易于阅读。在实际输出中不会有换行符。
如您所见,StringBuffer由输入文本中的字符和替换组成,每次匹配一个。
正则表达式语法
要在Java中有效地使用正则表达式,您需要了解语法。语法非常广泛,允许您编写非常高级的正则表达式。要完全掌握语法可能需要大量的练习。
在这篇文章中,我将通过例子来讲解语法的基本知识。我将不讨论语法的每一个细节,但将重点放在您需要理解的主要概念上,以便使用正则表达式。要获得完整的解释,请参阅模式类JavaDoc页面。
基本语法
在展示可以在Java正则表达式中使用的所有高级选项之前,我先简要介绍一下Java正则表达式语法基础。
Characters特性
正则表达式最基本的形式是只匹配特定字符的表达式。举个例子:
John
这个简单的正则表达式将匹配给定输入文本中出现的文本“John”。
您可以在正则表达式中使用字母表中的任何字符。
您还可以通过八进制、十六进制或unicode代码引用字符。这里有两个例子:
\0101
\x41
\u0041
这三个表达式都引用大写字符。第一个使用八进制代码(101)表示A,第二个使用十六进制代码(41),第三个使用unicode代码(0041)。
Character Classes字符类
字符类是constructst,它使您能够指定针对多个字符的匹配,而不是只针对一个字符。换句话说,字符类将输入文本中的单个字符与字符类中允许的多个字符匹配。例如,你可以像这样匹配字符a、b或c:
[abc]
字符类嵌套在一对方括号[]中。括号本身不是正在匹配的内容的一部分。
您可以为许多事情使用字符类。例如,这个例子找到了所有出现的单词John,要么是小写字母,要么是大写字母J:
[Jj]ohn
字符类[Jj]将匹配一个J或一个J,而表达式的其余部分将匹配字符ohn的确切顺序。
您还可以使用其他几个字符类。请参阅本文后面的字符类表。
Predefined Character Classes预定义的字符类
使用。例如,\d字符类匹配任何数字,\s字符类匹配任何空白字符,\w字符匹配任何单词字符。
预定义的字符类不必用方括号括起来,但是如果您想组合它们,可以这样做。这里有几个例子:
\d
[\d\s]
第一个示例匹配任何数字字符。第二个示例匹配任何数字或任何空格字符。
在本文后面的表格中列出了预定义的字符类。
Boundary Matchers边界匹配器
语法还包括匹配边界的匹配器,如单词之间的边界、输入文本的开头和结尾等。例如,\w匹配单词之间的边界,^匹配行首,$匹配行尾。
下面是一个边界匹配器的例子:
^This is a single line$
这个表达式匹配一行文本,只匹配一行文本。注意表达式中的起始行和结束行匹配器。这些语句表示,除了行首和行尾,文本前后不能有任何内容。
在本文后面有一个完整的边界匹配器列表。
Quantifiers量词
量词使您能够多次匹配给定的表达式或子表达式。例如,下面的表达式匹配字母A 0或更多次:
A*
*字符是一个量词,表示“0次或多次”。还有一个+量词,意思是“一次或多次”,a ?量词的意思是“零或一次”,还有一些其他的,你可以在本文后面的量词表中看到。
量词可以是“勉强的”、“贪婪的”或“占有的”。一个不情愿的量词将匹配尽可能少的输入文本。贪婪量词将尽可能匹配输入文本。一个所有量词将尽可能多地匹配,即使它使表达式的其余部分不匹配任何东西,并且使表达式无法找到匹配。
我将用一个例子来说明不情愿、贪婪和拥有量词之间的区别。这里是一个输入文本:
John went for a walk, and John fell down, and John hurt his knee.
然后看看下面这个带有不情愿的量词的表达:
John.*?
此表达式将匹配“John”后面跟零或多个字符的单词。表示“任意字符”,表示“0次或多次”。的吗?后面的使*成为一个不情愿的量词。
作为一个不情愿的量词,量词将匹配尽可能少,这意味着零字符。因此,表达式将在上面的输入文本中找到单词John后加上0个字符,共3次。
如果我们将量词改为贪婪量词,表达式将会是这样的:
John.*
贪心量词将匹配尽可能多的字符。现在表达式将只匹配第一次出现的John,贪婪量词将匹配输入文本中的其余字符。因此,只找到一个匹配项。
最后,让我们稍微修改一下表达式,使其包含一个所有量词:
John.*+hurt
*后面的+表示所有量词。
这个表达式将与上面给出的输入文本不匹配,即使在输入文本中同时找到John和hurt两个单词。这是为什么呢?因为。*+是所有的。与贪婪量词所做的尽可能多地匹配以使表达式匹配不同,拥有量词尽可能多地匹配,而不管表达式是否匹配。
.*+将匹配输入文本中第一个出现John之后的所有字符,包括单词hurt。因此,当所有量词已经声明了它的匹配项时,就没有需要匹配的伤害词了。
如果将量词更改为贪婪量词,则表达式将与输入文本匹配一次。下面是带贪心量词的表达式:
John.*hurt
您将不得不尝试不同的量词和类型,以了解它们是如何工作的。有关量词的完整列表,请参阅本文后面的表格。
Logical Operators逻辑运算符
Java正则表达式语法还支持一些逻辑操作符(and, or, not)。
and运算符是隐式的。当你写John这个表达式时,它的意思是“J和oh和n”。
r操作符是显式的,并且是用|编写的。例如,表达式John|hurt将匹配单词John或单词hurt。
Characters
Construct | 匹配 |
---|---|
x | 字母x。字母表中的任何字符都可以代替x。 |
\ | 反斜杠字符。单个反斜杠与其他字符一起用作转义字符,以表示特殊匹配,因此,要仅匹配反斜杠字符本身,需要使用反斜杠字符转义。因此,使用双反斜杠来匹配单个反斜杠字符。 |
\0n | 八进制值为0n的字符。n必须在0到7之间。 |
\0nn | 八进制值为0的字符。n必须在0到7之间。 |
\0mn | 八进制值为0mnn的字符。m必须在0和3之间,n必须在0和7之间。 |
\xhh | 十六进制值0xhh的字符。 |
\uhhh | 十六进制值0xhhhh的字符。这个结构用于匹配unicode字符。 |
\t | 制表符。 |
\n | 换行(换行)字符(unicode: ‘\u000A’)。 |
\r | 载波返回字符(unicode: ‘\u000D’)。 |
\f | 表单提要字符(unicode: ‘\u000C’)。 |
\a | 警告(铃声)字符(unicode: ‘\u0007’)。 |
\e | 转义字符(unicode: ‘\u001B’)。 |
\cx | 与x对应的控制字符 |
字符组
Construct | 匹配 |
---|---|
[abc] | 匹配a、b或c。这称为简单类,它匹配类中的任何字符。 |
[^abc] | 匹配除a、b和c之外的任何字符。这是一个否定。 |
[a-zA-Z] | 匹配从a到z或从A到Z的任何字符,包括a、A、z和Z。这是一个范围。 |
[a-d[m-p]] | 匹配从a到d或从m到p的任何字符。 |
[a-z&&[def]] | 匹配d、e或f。这称为交集(这里是范围a-z和字符def之间的交集)。 |
[a-z&& [ ^bc ] ] | 匹配从a到z的所有字符,除了b和c。这叫做减法。 |
[a-z&&[ ^m-p ]] | 匹配从a到z的所有字符,除了从m到p的字符。这也称为减法。 |
预定义的字符类
Construct | 匹配 |
---|---|
. | 匹配任何单个字符。可能匹配行终止符,也可能不匹配,这取决于用于编译模式的标志。 |
\d | 匹配任何数字[0-9] |
\D | 匹配任何非数字字符[^0-9] |
\s | 匹配任何空白字符(空格、制表符、换行符、回车符) |
\S | 匹配任何非空白字符。 |
\w | 匹配任何单词字符。 |
\W | 匹配任何非单词字符。 |
边界匹配器
Construct | 匹配 |
---|---|
^ | 匹配行首。 |
$ | 匹配行尾 |
\b | 匹配单词边界。 |
\B | 匹配非单词边界。 |
\A | 匹配输入文本的开头。 |
\G | 匹配上一场比赛的结束 |
\Z | 匹配输入文本的结尾,如果有最终终止符,则不匹配。 |
\z | 匹配输入文本的末尾。 |
量词
Greedy | Reluctant | Possessive | Matches |
---|---|---|---|
X? | X?? | X?+ | 匹配X一次,或者根本不匹配(0次或1次)。 |
X* | X*? | X*+ | 匹配X 0次或更多次。 |
X+ | X+? | X++ | 匹配X一次或多次。 |
X{n} | X{n}? | X{n}+ | 恰好匹配X n次。 |
X{n,} | X{n,}? | X{n,}+ | 匹配X至少n次。 |
X{n,m} | X[n,m]? | X{n,m}+ | 匹配X至少n次,但最多匹配m次。 |
逻辑运算
Construct | Matches |
---|---|
XY | 匹配X和Y (X后面跟着Y)。 |
X|Y | 匹配X或Y。 |
参考文献:
http://tutorials.jenkov.com/java-regex/index.html(采用机翻+部分本人翻译)