标题:
构建用于正则表达式的抽象 Java API(2)实践
[打印本页]
作者:
look_w
时间:
2018-7-15 08:15
标题:
构建用于正则表达式的抽象 Java API(2)实践
上机实践要在 Java 代码中使用正则表达式,通常需要完成以下七个步骤:
第 1 步:创建编译器实例。
如果使用 Jakarta Oro,则必须实例化 Perl5Compiler :
org.apache.oro.text.regex.Perl5Compiler compiler = new org.apache.oro.text.regex.Perl5Compiler();使用 RegexpPlugin 时的等同代码是相似的:
org.acmsl.regexpplugin.Compiler compiler = org.acmsl.regexpplugin.RegexpManager.createCompiler();但存在差异。正如前面提到的,该 API 对实际使用哪个具体实现加以隐藏。您可以选择一个具体实现或保留缺省的
Jakarta Oro
。如果所选的库在运行时不可用,则 RegexpPlugin API 会尝试用它的类名创建一个编译器。如果该操作失败,它会将异常发回 API 的客户机。
假定您一直在使用 JDK 1.4 的内置 regexp 类。那样的话,包含始终不会使用的额外 jar 文件毫无意义。那就是为什么仅仅调用 createCompiler() 方法还不够的原因。您需要管理这样的异常:每当所选的库不存在时就会抛出该异常。因而必须更新示例:
try{ org.acmsl.regexpplugin.Compiler compiler = org.acmsl.regexpplugin.RegexpManager.createCompiler();}catch (org.acmsl.regexpplugin.RegexpEngineNorFoundException exception){ [..]}
第 2 步:编译 regexp 模式。
将正则表达式本身编译到 Pattern 对象中。
org.apache.oro.text.regex.Pattern pattern = compiler.compile(".*\\s*(.*?)\\s+.*", Perl5Compiler.MULTILINE_MASK);注:您必须转义反斜杠(\)字符。
该模式对象代表以文本格式定义的正则表达式。请尽可能多地重用模式实例。然后,如果 regexp 是固定的(缺少任何可变部分,如
“(.*?)Tom.*”
),则模式应是类中的静态成员。
compile 方法适合用标志(如 EXTENDED_MASK )来配置(请参阅 以获得更详细的 regexp 教程)。但是,RegexpPlugin 并不允许随意的标志。受支持的标志只有 case sensitivity 和 multiline ,因为所有受支持的库都可以处理它们。
编译器实例有特定的特性来定义这些标志:
compiler.setMultiline(true);org.acmsl.regexpplugin.Pattern pattern = compiler.compile(".*\\s*(.*?)\\s+.*");
第 3 步:创建 Matcher 对象。
在
Jakarta Oro
中,这一步非常简单:
org.apache.oro.text.regex.Perl5Matcher matcher = new org.apache.oro.text.regex.Perl5Matcher();它之所以如此简单是因为它不需要构造任何信息。在后来的 regexp 中,它将变得具体。基本上,RegexpPlugin 中的步骤差不多相似。您不必亲自创建 matcher ,而是可以将其代理给 RegexpManager 类:
org.acmsl.regexpplugin.Matcher matcher = org.acmsl.regexpplugin.RegexpManager.createMatcher();区别和前面一样,您需要处理 RegexpEngineNotFoundException 。实际上, RegexpManager 需要为您所选的库或缺省库创建 matcher 适配器。如果这样的类在运行时不可用,它会抛出该异常。
第 4 步:评估正则表达式。
matcher 对象需要解释正则表达式并抽取所需的信息。这在一行代码中完成:
if (matcher.contains("John A. Smith", pattern)){如果输入文本与正则表达式匹配,则该方法返回
true
。隐含的副作用是,执行该行代码之后, matcher 对象包含在输入文本中找到的第一个匹配项。接下来的一步演示如何实际获取感兴趣的信息。
通过使用 RegexpPlugin API,在此时根本没有任何不同。
第 5 步:检索找到的第一个匹配项。
这一简单的步骤仅用一行完成:
org.apache.oro.text.regex.MatchResult matchResult = matcher.getMatch();您可以声明一个局部变量来存储这样的对象,该对象含有与 regexp 匹配的一段文本。在这两种情况下,该步骤是相同的,除了变量声明(因为一个是另一个的适配器):
org.acmsl.regexpplugin.MatchResult matchResult = matcher.getMatch();
第 6 步:获取感兴趣的 group 。
您可以使用两种方法:
具体库
RegexpPlugin API
因为您的 regexp 是 .*\s*(.*?)\s+.* ,所以您只有一个组: (.*?)
MatchResult 对象包含已排序列表中的所有组。您只需要知道要获取的组的位置。因为该示例只有一个组,所以毫无疑问:
String name = matchResult.group(1); [..]}变量 name 现在包含文本
John
,那正是您需要的。
第 7 步:如果需要,则重复该过程。
如果您需要的信息可多次出现,而您想分析所有出现的信息而不只是第一个,那么您只需循环执行第
5
步到第
7
步,直到不满足
第 4 步
中描述的条件为止:
while (matcher.contains("John A. Smith", pattern)){映射除了编写公共抽象 API,主要的工作实际上是实现 Java 环境中某些已存在的 regexp 引擎的适配器。
以下各表提供了对如何从一个库迁移至另一个库的详细描述。有些情况中,概念明显不同。也有些情况中,却不是那么明显。
Regexp 概念GNU Regexp 1.2编译器gnu.regexp.RE模式gnu.regexp.RE匹配程序gnu.regexp.REMatchEnumeration
gnu.regexp.RE匹配结果gnu.regexp.REMatch畸形模式异常gnu.regexp.REException
Regexp 概念Jakarta Oro 2.0.6编译器org.apache.oro.text.regex.Perl5Compiler模式org.apache.oro.text.regex.Pattern匹配程序org.apache.oro.text.regex.Perl5Matcher匹配结果org.apache.oro.text.regex.MatchResult畸形模式异常org.[..].regex.MalformedPatternException
Regexp 概念Jakarta Regexp 1.3编译器org.apache.regexp.RE
org.apache.regexp.RECompiler
org.apache.regexp.REProgram模式org.apache.regexp.REProgram
org.apache.regexp.RE匹配程序org.apache.regexp.RE
org.apache.regexp.REProgram匹配结果org.apache.regexp.RE畸形模式异常org.apache.regexp.RESyntaxException
Regexp 概念JDK 1.4 regex 包编译器java.util.regex.Pattern模式java.util.regex.Pattern匹配程序java.util.regex.Matcher匹配结果java.util.regex.Matcher畸形模式异常java.util.regex.PatternSyntaxException
基准该 API 较显著的用法之一是用来比较实现、测量性能、对 Perl5 语法的兼容性或其它标准之间的差异。
为这些测试开发的基准实用程序使用 HTML 解析器来处理 Web 内容,更新有关链接、表单和表等元素的信息。但是,重要的是解析逻辑用正则表达式来表示,因此会通过 RegexpPlugin API 实现。
基准测试包括对非常简单的 HTML 页面解析 10000 次。结果在下表中显示。
Regexp 库
Benchmark 结果(秒)
Jakarta Oro 2.0.6
130,71
Jakarta Regexp 1.2
23,261
GNU Regexp 1.1.4
1,966.939
JDK1.4
33,222
您可以用多种方法在实际应用程序中改进性能。最重要的是,当您使用 regexp 库时,不需要每次都编译模式,而是编译它们并重用各自的实例。但是,如果 regexp 本身不固定,则不能忽略编译过程。
因为基准需要在实现之间切换以比较性能,所以必须始终废弃已编译模式以避免库之间的交互。但是,正如您所见,大多数已评估的库有相似的响应时间,尽管更详细的基准能让我们更好的理解每个库在不同环境下的行为。
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0