正则表达式 基本来讲是一种模式,描述一组具有该共同模式的字符串。如果您是 Perl 程序员,应该非常熟悉 Java 语言中的正则表达式 (regex) 模式语法。但是,如果您不习惯使用正则表达式语法,它可能看起来很怪异。本节指导您在 Java 程序中使用正则表达式。
Regular Expressions API
下面是一组具有某些共性的字符串:
- A string
- A longer string
- A much longer string
注意,这些字符串中的每一个都以 a 开头,以 string 结尾。Java Regular Expressions API 可帮助您将这些元素提取出来,查看它们之间的模式,然后使用已收集的信息做有趣的工作。
Regular Expressions API 有 3 个您几乎总是在使用的核心类:
Pattern
描述一种字符串模式。Matcher
测试一个字符串,查看它是否与模式匹配。PatternSyntaxException
告诉您,无法接受您尝试定义的模式的某个方面。
很快您就会开始使用简单的正则表达式模式,该模式使用了这些类。但是在这么做之前,看看 regex 模式语法。
regex 模式语法
regex 模式 描述字符串的结构,表达式会尝试在输入字符串中查找该结构。这是正则表达式可能看起来有点奇怪的地方。但是,一旦您理解了该语法,就可以更轻松地解释它。表 1 列出了在模式字符串中使用的一些最常见的 regex 构造:
表 1. 常见 regex 构造
Regex 构造 | 符合匹配条件的内容 |
---|---|
. | 任何字符 |
? | 前面的零 (0) 或一 (1) 个字符或数字 |
* | 前面的零 (0) 或更多个字符或数字 |
+ | 前面的一 (1) 或更多个字符或数字 |
[] | 一个字符或数字范围 |
^ | 后面的条件的否定(即”非 后面的条件 ”) |
\d | 任何数字(也可表示为 [0-9] ) |
\D | 任何非数字(也可表示为 [^0-9] ) |
\s | 任何空格字符(也可表示为 [\n\t\f\r] ) |
\S | 任何非空格字符(也可表示为 [^\n\t\f\r] ) |
\w | 任何单词字符(也可表示为 [a-zA-Z_0-9] ) |
\W | 任何非单词字符(也可表示为 [^\w] ) |
前几种构造称为 量词 ,因为它们对之前的内容进行量化。\d
等构造是预定义的字符类。任何在一种模式中没有特殊含义的字符都是文字并与自身匹配。
模式匹配
掌握 表 1 中的模式语法后,就能理解清单 1 中的简单示例了,这个示例使用了 Java Regular Expressions API 中的类。
清单 1. 使用 regex 进行模式匹配
Pattern pattern = Pattern.compile("a.*string");
Matcher matcher = pattern.matcher("a string");
boolean didMatch = matcher.matches();
Logger.getAnonymousLogger().info (didMatch);
int patternStartIndex = matcher.start();
Logger.getAnonymousLogger().info (patternStartIndex);
int patternEndIndex = matcher.end();
Logger.getAnonymousLogger().info (patternEndIndex);
首先,清单 1 调用 compile()
( Pattern
上的一个静态方法)来创建一个 Pattern
类,并使用一个字符串文字来表示想要匹配的模式。该文字使用了 regex 模式语法。在本例中,该模式的中文翻译为:
_找到一个具有以下形式的字符串:A
或 a
后跟零或更多个字符,然后是 string
。
匹配方法
接下来,清单 1 在 Pattern
上调用 matcher()
。该调用创建一个 Matcher
实例。 然后 Matcher
搜索您传入的字符串,寻找与您在创建 Pattern
时使用的模式字符串相匹配的结果。
每个 Java 语言字符串都是一个带索引的字符集合,索引从 0 开始,到字符串长度减 1 结束。Matcher
从 0 开始解析该字符串,寻找与它匹配的结果。处理完成后,Matcher
包含有关在输入字符串中找到(或未找到)匹配结果的信息。可在 Matcher
上调用各种方法来访问该信息:
matches()
告诉您整个输入序列是否与该模式准确匹配。start()
告诉您匹配的字符串在输入字符串中开始处的索引值。end()
告诉您匹配的字符串在输入字符串中结束处的索引值加 1 的结果。
清单 1 找到了一个从 0 开始,到 7 结束的匹配结果。因此,调用 matches()
会返回 true
,调用 start()
会返回 0
,调用 end()
会返回 8
。
lookingAt() 与 matches()
如果字符串中的元素比搜索模式中的字符要多,可使用 lookingAt()
代替 matches()
。lookingAt()
搜索与给定模式匹配的子字符串。例如,考虑下面这个字符串:
Here is a string with more than just the pattern.
您可在此字符串中搜索 a.*string
,而且如果使用 lookingAt()
,就会获得一个匹配结果。但是如果使用 matches()
,就会返回 false
,因为字符串中包含的内容比模式中的多。
regex 中的复杂模式
可使用 regex 类轻松完成简单的搜索,同时也可使用 Regular Expressions API 执行非常复杂的操作。
Wiki 几乎完全基于正则表达式。Wiki 内容基于用户的字符串输入,并且使用正则表达式来解析和格式化该输入。 任何用户都可输入一个 wiki 词组,从而在 wiki 中创建另一个主题的链接,这个词组通常是一系列串联的单词,每个单词以一个大写字母开头,类似这样:
MyWikiWord
假设一个用户输入下面这个字符串:
Here is a WikiWord followed by AnotherWikiWord, then YetAnotherWikiWord.
您可使用 regex 模式在这个字符串中搜索 wiki 单词,类似这样:
[A-Z][a-z]*([A-Z][a-z]*)+
下面是搜索 wiki 单词的代码:
String input = "Here is a WikiWord followed by AnotherWikiWord, then SomeWikiWord.";
Pattern pattern = Pattern.compile("[A-Z][a-z]*([A-Z][a-z]*)+");
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
Logger.getAnonymousLogger().info("Found this wiki word:" + matcher.group());
}
运行此代码,应在控制台中看到 3 个 wiki 单词。
替换字符串
搜索匹配内容很有用,但也可在找到匹配字符串后操作它们。您可将匹配的字符串替换为其他字符串,就像在文字处理程序中搜索一些文本并将它替换为其他文本一样。Matcher
有两个替换字符串元素的方法:
replaceAll()
将所有匹配值替换为一个指定的字符串。replaceFirst()
仅将第一个匹配值替换为一个指定的字符串。
使用 Matcher
的 replace
方法很简单:
String input = "Here is a WikiWord followed by AnotherWikiWord, then SomeWikiWord.";
Pattern pattern = Pattern.compile("[A-Z][a-z]*([A-Z][a-z]*)+");
Matcher matcher = pattern.matcher(input);
Logger.getAnonymousLogger().info("Before:" + input);
String result = matcher.replaceAll("replacement");
Logger.getAnonymousLogger().info("After:" + result);
与之前一样,此代码会查找 wiki 单词。Matcher
找到一个匹配值时,它会将该 wiki 单词文本替换为相应的替换值。运行该代码时,可在控制台上看到以下消息:
Before:Here is WikiWord followed by AnotherWikiWord, then SomeWikiWord.
After:Here is replacement followed by replacement, then replacement.
如果使用 replaceFirst()
,则会看到此消息:
Before:Here is a WikiWord followed by AnotherWikiWord, then SomeWikiWord.
After:Here is a replacement followed by AnotherWikiWord, then SomeWikiWord.
匹配和操作分组
搜索一个 regex 模式的匹配结果时,可获得有关找到的结果的信息。您已在 Matcher
的 start()
和 end()
方法上看到过该功能。但也可以通过捕获 分组 来引用匹配值。
在每种模式中,通常通过将模式的各部分放在圆括号中来创建分组。分组从左向右编号,从 1 开始编号(分组 0 表示完整的匹配结果)。清单 2 中的代码将每个 wiki 单词替换为一个“包含”该单词的字符串: