使用 Regex API 简化常见编程任务
在本文的第 1 部分和第 2 部分中,我们向您介绍了正则表达式和 Regex API。您了解了该类Pattern
,并浏览了演示正则表达式构造的示例,从使用文字字符串的简单模式匹配到使用范围、边界匹配器和量词的更复杂的匹配。在本部分和后续部分中,我们将考虑第一部分中未涵盖的问题,我们将研究类Pattern
、Matcher
和的相应方法PatternSyntaxException
。您还将学习两个使用正则表达式来简化常见编程问题的实用程序。第一个从代码中提取注释以用于文档。第二个是可重用代码库,旨在执行词法分析 - 汇编器、编译器和类似软件的重要组成部分。
下载源代码
您可以从此处获取本文中演示应用程序的所有源代码(由 Jeff Friesen 为 JavaWorld 创建)。学习正则表达式 API
Pattern
、Matcher
和PatternSyntaxException
是构成 Regex API 的三个类。它们每个都提供允许您在代码中使用正则表达式的方法。
Pattern 类的方法
类的实例Pattern
是编译后的正则表达式,也称为模式。编译正则表达式是为了提高模式匹配操作的性能。以下静态方法支持编译。
Pattern compile(String regex)
将内容编译regex
为存储在新的Pattern
. 如果成功,此方法返回对对象的引用;PatternSyntaxException
如果检测到无效的正则表达式语法,则抛出异常。Matcher
该对象使用或返回的类的任何对象都Pattern
使用其默认设置,例如区分大小写的搜索。例如,代码片段Pattern p = Pattern.compile("(?m)^\\.");
创建一个对象Pattern
,该对象存储正则表达式的编译表示形式,以匹配以点字符开头的字符串。Pattern compile(String regex, int flags)
解决与 相同的问题Pattern compile(String regex)
,但考虑到flags
: OR 类型的位标志的一组位常量。该类声明可以使用按位 OR(例如)组合并作为参数传递的Pattern
常量。CANON_EQ, CASE_INSENSITIVE, COMMENTS, DOTALL, LITERAL, MULTILINE, UNICODE_CASE, UNICODE_CHARACTER_CLASS и UNIX_LINES
CASE_INSENSITIVE | DOTALL
flags
除了 之外
CANON_EQ, LITERAL и UNICODE_CHARACTER_CLASS
,这些常量是第 1 部分中演示的嵌套标志表达式的替代方案。如果遇到类中定义的标志常量以外的标志常量Pattern
,该方法Pattern compile(String regex, int flags)
将引发异常java.lang.IllegalArgumentException
。例如,Pattern p = Pattern.compile("^\\.", Pattern.MULTILINE);
相当于前面的示例,常量Pattern.MULTILINE
和嵌套标志表达式(?m)
执行相同的操作。
Pattern
及其使用的标志。为此,您可以调用以下方法:
String pattern()
返回编译成Pattern
.int flags()
返回对象的 flagsPattern
。
Pattern
,通常用于获取对象Matcher
来进行模式匹配操作。该方法Matcher matcher(Charsequence input)
创建一个对象Matcher
,该对象在文本中搜索input
与对象模式的匹配项Pattern
。调用时,它返回对此对象的引用Matcher
。例如,该命令Matcher m = p.matcher(args[1]);
返回变量 引用的Matcher
对象。 Pattern
p
一次性搜索 |
---|
static boolean matches(String regex, CharSequence input) 类方法Pattern 允许您节省创建对象Pattern 和Matcher 使用模板进行一次性搜索的时间。input 如果模式匹配,此方法返回 true regex ,否则返回 false。如果正则表达式包含语法错误,该方法将引发异常PatternSyntaxException 。例如,System.out.println(Pattern.matches("[a-z[\\s]]*", "all lowercase letters and whitespace only")); 打印true ,确认该短语all lowercase letters and whitespace only 仅包含空格和小写字符。 |
分割文本
大多数开发人员至少编写过一次代码来将输入文本分解为其组成部分,例如将基于文本的员工帐户转换为一组字段。该类Pattern
提供了使用两种文本分割方法更方便地解决这项繁琐任务的能力:
-
该方法根据找到的对象模式匹配
String[] split(CharSequence text, int limit)
进行拆分,并以数组形式返回结果。每个数组元素指定一个文本序列,通过模式匹配文本片段(或文本结尾)与下一个序列分隔开。数组元素的顺序与它们在 中出现的顺序相同。text
Pattern
text
在此方法中,数组元素的数量取决于参数
limit
,该参数还控制要查找的匹配项的数量。- 正值搜索不超过
limit-1
匹配项,并且数组的长度不超过limit
元素。 - 如果值为负数,则搜索所有可能的匹配项,并且数组的长度可以是任意的。
- 如果该值为零,则搜索所有可能的匹配项,数组的长度可以是任意的,并且末尾的空行将被丢弃。
- 正值搜索不超过
- 该方法
String[] split(CharSequence text)
使用 0 作为 limit 参数调用前一个方法并返回其调用结果。
split(CharSequence text)
下面是解决将员工帐户拆分为姓名、年龄、邮政地址和工资字段的问题 的方法的结果:
Pattern p = Pattern.compile(",\\s");
String[] fields = p.split("John Doe, 47, Hillsboro Road, 32000");
for (int i = 0; i < fields.length; i++)
System.out.println(fields[i]);
上面的代码描述了一个正则表达式,用于查找紧跟单个空格字符的逗号字符。以下是其执行结果:
John Doe
47
Hillsboro Road
32000
模板谓词和 Streams API
在Java 8中,类中Pattern
出现了方法。此方法创建一个用于匹配模式的谓词(具有布尔值的函数)。该方法的使用如下面的代码片段所示: Predicate
asPredicate()
List progLangs = Arrays.asList("apl", "basic", "c", "c++", "c#", "cobol", "java", "javascript", "perl", "python", "scala");
Pattern p = Pattern.compile("^c");
progLangs.stream().filter(p.asPredicate()).forEach(System.out::println);
此代码创建一个编程语言名称列表,然后编译一个模式以查找以字母 开头的所有名称c
。上面的最后一行代码实现了接收以此列表为源的串行数据流。它使用布尔函数设置一个过滤器asPredicate()
,当名称以字母开头时返回 true c
,并迭代流,将匹配的名称打印到标准输出。最后一行相当于以下常规循环,在第 1 部分的 RegexDemo 应用程序中很常见:
for (String progLang: progLangs)
if (p.matcher(progLang).find())
System.out.println(progLang);
匹配器类方法
该类的实例Matcher
描述了一种通过解释该类的已编译正则表达式对字符序列执行模式匹配操作的机制Pattern
。该类的对象Matcher
支持各种类型的模式搜索操作:
-
该方法
boolean find()
在输入文本中搜索下一个匹配项。此方法从指定文本的开头或上一个匹配项之后的第一个字符开始扫描。仅当先前对此方法的调用返回 true 并且解析器未重置时,才可以使用第二个选项。无论如何,如果搜索成功,则返回布尔值 true。此方法的示例可在RegexDemo
第 1 部分中找到。 -
该方法
boolean find(int start)
重置匹配器并在文本中搜索下一个匹配项。从参数指定的位置开始查看start
。如果搜索成功,则返回布尔值true。例如,m.find(1);
从位置开始扫描文本1
(忽略位置 0)。如果参数start
包含负值或大于匹配器文本长度的值,该方法将引发异常java.lang.IndexOutOfBoundsException
。 -
该方法
boolean matches()
尝试将所有文本与模式匹配。如果所有文本都与该模式匹配,它将返回一个布尔值 true。例如,代码Pattern p = Pattern.compile("\\w*"); Matcher m = p.matcher("abc!"); System.out.println(p.matches());
输出是false
因为该字符!
不是单词字符。 -
该方法
boolean lookingAt()
尝试将指定的文本与模式进行匹配。如果文本的任何部分与模式匹配,则此方法返回 true。与方法不同matches();
,所有文本不必与模式匹配。例如,Pattern p = Pattern.compile("\\w*"); Matcher m = p.matcher("abc!"); System.out.println(p.lookingAt());
它将输出true
,因为文本的开头abc!
仅包含构词字符。
Pattern
,类对象Matcher
保留状态信息。有时,您可能需要在模式搜索完成后重置匹配器以清除此信息。可以使用以下方法来重置解析器:
-
该方法
Matcher reset()
重置匹配器的状态,包括要附加到末尾的位置(重置为 0)。下一个模式搜索操作从匹配器文本的开头开始。返回对当前对象的引用Matcher
。例如,m.reset();
重置 引用的解析器m
。 -
该方法
Matcher reset(CharSequence text)
重置解析器状态并将新解析器文本设置为text
。下一个模式搜索操作从新匹配器文本的开头开始。返回对当前对象的引用Matcher
。例如,m.reset("new text");
重置引用的解析器m
并将新解析器文本设置为"new text"
。
添加文本到末尾
要附加到末尾的匹配器的位置指定附加到 类型的对象末尾的匹配器文本的开头java.lang.StringBuffer
。以下方法使用该位置:
-
该方法读取匹配器文本字符并将它们附加到参数引用的
Matcher appendReplacement(StringBuffer sb, String replacement)
对象的末尾。此方法在上一个模式匹配之前的最后一个字符处停止读取。接下来,该方法将参数引用类型的对象中的字符附加到对象的末尾(字符串可能包含对先前搜索期间捕获的文本序列的引用;这些是使用捕获的字符和组编号指定的)。最后,该方法将匹配器位置的值设置为附加到最后一个匹配字符的位置加一,然后返回对当前匹配器的引用。StringBuffer
sb
String
replacement
StringBuffer
replacement
($)
-
该方法
StringBuffer appendTail(StringBuffer sb)
将所有文本添加到一个对象StringBuffer
并返回对该对象的引用。在最后一个方法调用之后appendReplacement(StringBuffer sb, String replacement)
,调用该方法appendTail(StringBuffer sb)
将剩余的文本复制到对象中StringBuffer
。
如果匹配器尚未找到匹配项或先前的搜索尝试失败,则该方法Matcher appendReplacement(StringBuffer sb, String replacement)
将引发异常。如果该行指定了不在模式中的捕获组,java.lang.IllegalStateException
则会引发异常)。IndexOutOfBoundsException
replacement
捕获的群体 |
---|
正如您在第 1 部分中所记得的那样,捕获组是括在括号 ( () ) 元字符中的字符序列。此构造的目的是存储找到的字符,以便以后在模式匹配期间重用。在模式搜索过程中,捕获组中的所有字符都被视为一个整体。 |
appendReplacement(StringBuffer sb, String replacement)
和方法appendTail(StringBuffer sb
,将源文本中出现的所有字符序列替换cat
为caterpillar
:
Pattern p = Pattern.compile("(cat)");
Matcher m = p.matcher("one cat, two cats, or three cats on a fence");
StringBuffer sb = new StringBuffer();
while (m.find())
m.appendReplacement(sb, "$1erpillar");
m.appendTail(sb);
System.out.println(sb);
在替换文本中使用捕获的组和对其的引用告诉程序erpillar
在每次出现cat
. 执行这段代码的结果如下所示: one caterpillar, two caterpillars, or three caterpillars on a fence
替换文本
该类Matcher
为我们提供了两种文本替换方法,与appendReplacement(StringBuffer sb, String replacement)
. 使用这些方法,您可以替换第一次出现的 [replaced text] 或所有出现的内容:
-
该方法
String replaceFirst(String replacement)
重置匹配器,创建一个新对象String
,将匹配器文本的所有字符(直到第一个匹配项)复制到此字符串,将字符从 追加到其末尾replacement
,将剩余字符复制到字符串并返回对象String
(该字符串replacement
可以包含对在先前搜索文本序列中使用美元符号和捕获的组编号捕获的引用)。 -
该方法的
String replaceAll(String replacement)
操作与 方法类似String replaceFirst(String replacement)
,但将replacement
所有找到的匹配项替换为字符串中的字符。
\s+
在输入文本中搜索一个或多个空白字符。下面,我们将使用这个正则表达式并调用一个方法replaceAll(String replacement)
来删除重复的空格:
Pattern p = Pattern.compile("\\s+");
Matcher m = p.matcher("Удаляем \t\t лишние пробелы. ");
System.out.println(m.replaceAll(" "));
结果如下: Удаляем лишние пробелы.
Java 中的正则表达式,第 4 部分 Java 中的正则表达式,第 5 部分
GO TO FULL VERSION