知识大全 构建用于正则表达式的抽象Java API

Posted

篇首语:人的天职在勇于探索真理。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 构建用于正则表达式的抽象Java API相关的知识,希望对你有一定的参考价值。

构建用于正则表达式的抽象Java API  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  在我的经验中 大多数 Java 开发人员都需要解析某种文本 通常 这意味着他们最初要花一些时间使用象 indexOf 或 substring 那样的与 Java 字符串相关的函数或方法 并且希望输入格式永远不变 但是 如果输入格式改变 那么用于读取新格式的代码维护起来就会变得更复杂 更困难 最后 代码可能需要支持自动换行(word wrapping) 区分大小写等

  由于逻辑变得更加复杂 所以维护也变得很困难 因为任何更改都可能产生副作用并使文本解析器的其它部分停止工作 所以开发人员需要时间修正这些小错误

  有一定 Perl 经验的开发人员可能也有过使用正则表达式的经验 如果够幸运(或优秀)的话 这位开发人员能够说服团队其余的人(或至少是团队领导)使用这项技术 新的方法将取消编写用来调用 String 方法的多行代码 它意味着将解析器逻辑的核心委托出去 并替换为 regexp 库

  接受了有 Perl 经验的开发人员的建议后 团队必须选择哪个 regex 实现最适合他们的项目 然后他们需要学习如何使用它

  在简要地研究了从因特网上找到的众多可选方案后 假设团队决定从人们更熟悉的库中选择一个使用 如属于 Jakarta 项目的 Oro 接下来 对解析器进行较大程度地重构或几乎重新编写 并且解析器最终使用了 Oro 的类 如 Perl Compiler Perl Matcher 等

  这一决定的后果很明显 代码与 Jakarta Oro 的类紧密地耦合在一起

  团队承担了风险 因为不知道非功能性需求(如性能或线程模型)是否将得到满足

  团队已花费时间和财力来学习并重新编写代码 以使它使用 regexp 库 如果他们的决定是错误的并且选择了新的库 则这一工作在成本上将不会有很大区别 因为将需要再次重新编写代码

  即使库工作正常 如果他们决定应该迁移到全新的库(例如 包括在 JDK 中的库) 怎么办?

  去耦的好处

  有没有办法使团队知道哪个实现最适合他们的需要呢(不仅现在能将来也能)?让我们试着寻找答案

  避免依赖任何特定的实现

  前面的情形在软件工程中十分常见 在有些情况中 这样的情形会导致较大的投资和较长的延期 当不了解所有后果就作出决定而且决策制定人不太走运或缺乏必需的经验时 就常常会发生这种情况

  可将该情形概括如下

  您需要某种提供者

  您没有选择最佳提供者的客观标准

  您希望能用最低的成本来评估所有的待选项

  所作的决定不应将您束缚在所选的提供者上

  这一问题的解决方法是使代码更加独立于提供者 这引入了新的层 ? 同时去除客户机和提供者的耦合的层

  在服务器端开发中 很容易找到使用该方法的模式或体系结构 下面引用一些示例

  对于 J EE 您主要关注如何构建应用程序而不是应用程序服务器的细节

  数据访问对象(Data Access Object DAO)模式隐藏了如何访问数据库(或 LDAP 服务器 XML 文件等)的细节和复杂性 因为它提供了访问抽象持久存储层的方法 而您则不需要在客户机代码中处理数据库问题(数据实际存储在哪里) 这不是四人组(Gang of Four GoF)模式 而是 Sun 的 J EE 最佳实践的一部分

  在假想的开发团队示例中 他们正在寻找这样的层

  抽象所有正则表达式实现背后的概念 团队就可以着重学习和理解这些概念 他们所学的可以应用到任何实现或版本

  支持新的库且没有副作用 基于插件体系结构 动态选择执行 regexp 模式的实际库 并且适配器不会被耦合 新库仅会引入对新适配器的需要

  提供比较不同可选方案的方法 一个简单的基准实用程序就可以显示有趣的性能测量结果 如果对每个实现都执行这样的实用程序 团队就会获得有价值的信息并能选择最好的可选方案

  听起来不错 但……

  任何去耦方法都至少有一个缺点 如果客户机代码仅需要一个实现所提供的特定功能 怎么办?您不能使用任何其它实现 因此您最终将代码与该实现耦合 也许将来会在这方面有所改善 但您现在却束手无策

  这样的示例并不象您想的那样少 在 regexp 领域中 一些编译器选项仅被某些实现支持 如果您的客户机代码需要这种特定的功能 那么这个一般层是不够的 ? 至少从迄今对它描述来看是不够的

  附加层是否应支持每个实现的所有非公共功能 并且如果选择了不支持该实现的附加层则抛出异常?那可以是一种解决方案 但它并不支持仅定义公共抽象概念这一最初目标

  有一个 GoF 模式非常适合这种情形 职责链(Chain of Responsibility) 它在设计中引入了另一种间接方法 用这种方法 客户机代码向能处理其所发消息的实体列表发送消息或命令 列表项被组织成链 因此消息可按顺序被处理并且在到达链尾之前被用掉

  在这种情况中 可以通过特殊类型的消息对仅被某些实现支持的特定功能建模 由链中的每一项根据其是否了解这些功能来决定是否将该消息传给下一项

  定义一个公共 API

  这里讲述的 API 名为 RegexpPlugin 已将它设计成遵循刚刚讨论的方法 并且它在 regexp 库和使用该库的代码之间支持去耦

  RegexpPlugin

  在以下示例中 我将总结一下使用具体实现(Jakarta Oro)和使用 RegexpPlugin API 之间的差别

  我从一个非常简单的 regexp 开始 假定您必须要解析的文本只是人名 您接收的格式是象 John A Smith 这样的内容 而您只想获取名字(John) 但您不知道单词由什么分隔 是空格 换行符 制表符还是这些字符的组合 能处理这样的输入格式的 regexp 只是 *s*( *?)s+ * 我将一步一步地说明如何使用该 regexp 来抽取信息

  第一部分是点号和星号字符 * 它们在这里表示任意数量的空格和 ( *?) 组之前的任何字符 第二部分比较引人注意(因为它被圆括号括起来) 问号表示取第一个符合条件的项

  接下来的符号表示任意数量的空格 换行或制表符(s) 但至少要有一个(+) 最后的点号和星号 * 仅代表文本的余下部分(对它没有兴趣)

  因此 该 regexp 相当于 取空格前的第一段文本 让我们来编写 Java 代码

  上机实践

  要在 Java 代码中使用正则表达式 通常需要完成以下七个步骤

  第 步 创建编译器实例 如果使用 Jakarta Oro 则必须实例化 Perl Compiler

   apache oro text regex Perl Compiler piler =new apache oro text regex Perl Compiler();

  使用 RegexpPlugin 时的等同代码是相似的

   acmsl regexpplugin Compiler piler = acmsl regexpplugin RegexpManager createCompiler();

  但存在差异 正如前面提到的 该 API 对实际使用哪个具体实现加以隐藏 您可以选择一个具体实现或保留缺省的 Jakarta Oro 如果所选的库在运行时不可用 则 RegexpPlugin API 会尝试用它的类名创建一个编译器 如果该操作失败 它会将异常发回 API 的客户机

  假定您一直在使用 JDK 的内置 regexp 类 那样的话 包含始终不会使用的额外 jar 文件毫无意义 那就是为什么仅仅调用 createCompiler() 方法还不够的原因 您需要管理这样的异常 每当所选的库不存在时就会抛出该异常 因而必须更新示例

  try

  

   acmsl regexpplugin Compiler piler = acmsl regexpplugin RegexpManager createCompiler();

  

  catch ( acmsl regexpplugin RegexpEngineNorFoundException exception)

  

  [ ]

  

  第 步 编译 regexp 模式 将正则表达式本身编译到 Pattern 对象中

   apache oro text regex Pattern pattern =pile( *\\s*( *?)\\s+ * Perl Compiler MULTILINE_MASK);

  注 您必须转义反斜杠()字符

  该模式对象代表以文本格式定义的正则表达式 请尽可能多地重用模式实例 然后 如果 regexp 是固定的(缺少任何可变部分 如 ( *?)Tom * ) 则模式应是类中的静态成员

  pile 方法适合用标志(如 EXTENDED_MASK)来配置(请参阅参考资料以获得更详细的 regexp 教程) 但是 RegexpPlugin 并不允许随意的标志 受支持的标志只有 case sensitivity 和 multiline 因为所有受支持的库都可以处理它们

  编译器实例有特定的特性来定义这些标志

  piler setMultiline(true);

   acmsl regexpplugin Pattern pattern =pile( *\\s*( *?)\\s+ * );

  第 步 创建 Matcher 对象 在 Jakarta Oro 中 这一步非常简单

   apache oro text regex Perl Matcher matcher =new apache oro text regex Perl Matcher();

  它之所以如此简单是因为它不需要构造任何信息 在后来的 regexp 中 它将变得具体 基本上 RegexpPlugin 中的步骤差不多相似 您不必亲自创建 matcher 而是可以将其代理给 RegexpManager 类

   acmsl regexpplugin Matcher matcher = acmsl regexpplugin RegexpManager createMatcher();

  区别和前面一样 您需要处理 RegexpEngineNotFoundException 实际上 RegexpManager 需要为您所选的库或缺省库创建 matcher 适配器 如果这样的类在运行时不可用 它会抛出该异常

  第 步 评估正则表达式 matcher 对象需要解释正则表达式并抽取所需的信息 这在一行代码中完成

  if (ntains( John A Smith pattern))

  如果输入文本与正则表达式匹配 则该方法返回 true 隐含的副作用是 执行该行代码之后 matcher 对象包含在输入文本中找到的第一个匹配项 接下来的一步演示如何实际获取感兴趣的信息

  通过使用 RegexpPlugin API 在此时根本没有任何不同

  第 步 检索找到的第一个匹配项 这一简单的步骤仅用一行完成

   apache oro text regex MatchResult matchResult = matcher getMatch();

  您可以声明一个局部变量来存储这样的对象 该对象含有与 regexp 匹配的一段文本 在这两种情况下 该步骤是相同的 除了变量声明(因为一个是另一个的适配器)

   acmsl regexpplugin MatchResult matchResult =matcher getMatch();

  第 步 获取感兴趣的 group 您可以使用两种方法

  具体库

  RegexpPlugin API

  因为您的 regexp 是 *s*( *?)s+ * 所以您只有一个组 ( *?)

  MatchResult 对象包含已排序列表中的所有组 您只需要知道要获取的组的位置 因为该示例只有一个组 所以毫无疑问

  String name = matchResult group( );

  [ ]

  

  变量 name 现在包含文本 John 那正是您需要的

  第 步 如果需要 则重复该过程 如果您需要的信息可多次出现 而您想分析所有出现的信息而不只是第一个 那么您只需循环执行第 步到第 步 直到不满足第 步中描述的条件为止

  while (ntains( John A Smith pattern))

  映射

  除了编写公共抽象 API 主要的工作实际上是实现 Java 环境中某些已存在的 regexp 引擎的适配器

  以下各表提供了对如何从一个库迁移至另一个库的详细描述 有些情况中 概念明显不同 也有些情况中 却不是那么明显

  Regexp 概念 GNU Regexp

  编译器 gnu regexp RE

  模式 gnu regexp RE

  匹配程序 gnu regexp REMatchEnumeration

  gnu regexp RE

  匹配结果 gnu regexp REMatch

  畸形模式异常 gnu regexp REException

  Regexp 概念 Jakarta Oro

  编译器 apache oro text regex Perl Compiler

  模式 apache oro text regex Pattern

  匹配程序 apache oro text regex Perl Matcher

  匹配结果 apache oro text regex MatchResult

  畸形模式异常 [ ] regex MalformedPatternException

  Regexp 概念 Jakarta Regexp

  编译器 apache regexp RE

   apache regexp RECompiler

   apache regexp REProgram

  模式 apache regexp REProgram

   apache regexp RE

  匹配程序 apache regexp RE

   apache regexp REProgram

  匹配结果 apache regexp RE

  畸形模式异常 apache regexp RESyntaxException

  Regexp 概念 JDK regex 包

  编译器 java util regex Pattern

  模式 java util regex Pattern

  匹配程序 java util regex Matcher

  匹配结果 java util regex Matcher

  畸形模式异常 java util regex PatternSyntaxException

  基准

  该 API 较显著的用法之一是用来比较实现 测量性能 对 Perl 语法的兼容性或其它标准之间的差异

  为这些测试开发的基准实用程序使用 HTML 解析器来处理 Web 内容 更新有关链接 表单和表等元素的信息 但是 重要的是解析逻辑用正则表达式来表示 因此会通过 RegexpPlugin API 实现

  基准测试包括对非常简单的 HTML 页面解析 次 结果在下表中显示

  Regexp 库 Benchmark 结果(秒)

  Jakarta Oro

  Jakarta Regexp

  GNU Regexp

  JDK

  您可以用多种方法在实际应用程序中改进性能 最重要的是 当您使用 regexp 库时 不需要每次都编译模式 而是编译它们并重用各自的实例 但是 如果 regexp 本身不固定 则不能忽略编译过程

  因为基准需要在实现之间切换以比较性能 所以必须始终废弃已编译模式以避免库之间的交互 但是 正如您所见 大多数已评估的库有相似的响应时间 尽管更详细的基准能让我们更好的理解每个库在不同环境下的行为

  结束语

cha138/Article/program/Java/hx/201311/26533

相关参考

知识大全 Java正则表达式详解(中)

Java正则表达式详解(中)  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!    三应用实例   

知识大全 JAVA 正则表达式4种常用的功能

JAVA正则表达式4种常用的功能  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!正则表达式在字符串处

知识大全 Java正则表达式详解(下)

Java正则表达式详解(下)  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  HTML处理实例二 

知识大全 Java正则表达式详解(上)

Java正则表达式详解(上)  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  如果你曾经用过Per

知识大全 java正则表达式非匹配

  四非匹配  在正则表达式中我们往往需要在字符串中进行非匹配这时就要通过^进行匹配条件限制^的常见入门用法如下:  [^az]    条件限制在非小写atoz

知识大全 Java中的17种常用正则表达式归纳

Java中的17种常用正则表达式归纳  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  ^\\\\d

知识大全 Java中使用正则表达式验证本地化数据

Java中使用正则表达式验证本地化数据  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!数据验证是编写

知识大全 常用正则表达式

  说明正则表达式通常用于两种任务验证搜索/替换用于验证时通常需要在前后分别加上^和$以匹配整个待验证字符串;搜索/替换时是否加上此限定则根据搜索的要求而定此外也有可能要在前后加上b而不是^和$此表所

知识大全 正则表达式的基本语法

cha138/Article/program/Java/hx/201311/26752

知识大全 php小经验:解析preg

  正则表达式在PHP中的应用在PHP应用中正则表达式主要用于•正则匹配根据正则表达式匹配相应的内容•正则替换根据正则表达式匹配内容并替换•正则分割根据正则表达式分割字符串在PHP中有两类正则表达式函