读书人

Antlr 和文本处理【缘于网络】

发布时间: 2012-10-28 09:54:44 作者: rapoo

Antlr 和文本处理【源于网络】

在使用 Antlr 处理文本时, 我们主要使用词法分析器(Lexer)来完成工作。在处理文本时, 文本本身是一个字符流。Antlr 的词法分析器是一个强大的文本处理工具,它能够把字符流依据词法规则分解成不同的记号,形成记号流,供后续处理。

文本处理工作在我们的日常工作中非常普遍,最常见的是日志分析,比如对应用服务器控制台输出日志进行分析,对应用自身的系统日志进行分析等等;其他一些时候我们甚至会把配置文件、源程序文件、XML 文件、HTML 文件等当成文本进行处理

?

在定义 SqlFrg 时引用的各个片段记号 (fragment tokens) 可以赋值为不同的变量,如清单 7 中的 t、c1、c2、v1、v2,这些变量在语义动作中可以直接使用,调用它们的 getText() 方法,即可得到各自在文本中对应的匹配内容。

完成 SqlTranslator 的定义后, 运行 java org.antlr.Tool SqlTranslator.g,由 Antlr 生成词法分析器 SqlTranslator.java,SqlTranslator.java 就是我们需要的转换器。调用 Antlr 提供的运行时 API,为抽取器编写以下测试代码,如清单 8 所示,至此完成了一个完整的转换器的例子。测试代码和抽取器几乎一致,唯一的区别是把抽取器换成了转换器。


清单 8. 转换器的测试代码

  public static void main(String[] args) throws Exception {  String filename = "errsql.out";  InputStream in = new FileInputStream(filename);  ANTLRInputStream input = new ANTLRInputStream(in);  SqlTranslator lexer = new SqlTranslator(input);  CommonTokenStream tokens = new CommonTokenStream(lexer);  for (Object obj : tokens.getTokens())  ;  } 

?

重写器 Rewriter

介绍

重写器是这样一类转换器,除了完成和特定目标的匹配转换外,其他未匹配的文本,原封不动的输出出来。除了对匹配项的转换外,重写器的输出文本和输入文本几乎一模一样。

一个例子

这里介绍一个对 HTTP 输出 (HTTP Response) 进行重写的例子。在重写器中,我们将所有的 URL 匹配出来进行了转换,其他非 URL 文本原封不动的输出到客户端。对 URL 转换的目的是将所有的 URL 都转发到一个全局的服务端组件去处理,在我们的实际应用中这样做的目的是要在应用服务器集群内部做一个 HTTP 代理,便于对集群中各个服务器的运行情况进行监控,跨过负载均衡设备的请求分发。读者不必对这个细节做过多的分析,只需了解重写器的输入是 HTTP 响应,重写器的输出仍然是 HTTP 响应,只是对输入中的 URL 做了某些转换。

创建词法文件 UrlRewriter.g,根据 URL 的词法特性定义 URL 的匹配规则。如清单 9 所示。


清单 9. URL 的词法定义

 fragment Url: SEPRATOR ('/'ID)+ ('.'Postfix)? ('?' ( options {greedy=false;} : . )* )? SEPRATOR;  fragment  SEPRATOR : '"' | '\'';  fragment  ID : ('a'..'z' |'A'..'Z' |'_' ) ('a'..'z' |'A'..'Z' |'_' |'0'..'9' |'-'| '.')*  ;  fragment  Postfix: ('jsp'|'js'|'action'|'html'|'htm'|'css'); 

?

定义了 SEPRATOR、ID、Postfix 和 Url 四个词法片段,共同完成了对 URL 的定义。现在我们只需定义词法匹配 Url 并同时完成对 URL 的转换。转换的方式是将所有的 URL 都替换为一个新的固定的 URL,并将原 URL 作为这个 URL 的第一个参数,原 URL 其他参数成为新 URL 的后续参数。如清单 10 所示。


清单 10. URL 的转换

  URL:u=Url     {    String url = u.getText(); url=url.replace("?", "&");  url=url.substring(0,1) + "/infrastructure/ProxyAction_forward.action?&url="       + url.substring(1); try{ out.write(url.getBytes());}catch(IOException e){e.printStackTrace();}  } ; 

?

到此为止,我们所完成的和前一节的转换器没有太多的区别。完成重写器的关键在于后续的词法定义,匹配其他所有非 URL 文本,以及对非 URL 文本的输出。这里我们定义一个词法匹配任意文本,如清单 11 所示。


清单 11. 非 URL 的匹配

  Other:c=AnyChar { try{ out.write($c.getText().getBytes()); } catch(IOException e){ e.printStackTrace();} };  fragment  AnyChar : . ; 

?

非 URL 的词法定义通过 AnyChar 来体现,它的定义非常简单,用通配符 (.) 表示匹配任意字符。Other 调用了 AnyChar,并把匹配结果直接写入到输出流中。

重写器的词法定义基本结束了,有一个尤其需要注意的是,词法 URL 和 Other 的定义顺序非常重要。如果先定义 Other 后定义 URL,最终的输出和输入完全一致,没有做任何的转换,从而也没有达到重写的目的。只有先定义 URL 后定义 Other,才能达到我们的目的。出现这一现象的原因是,Antlr 中词法定义,包括语法定义是按照定义的先后顺序去做匹配的,优先定义的规则将优先匹配。Other 规则的定义能够匹配 URL 规则定义的所有内容,所以当 Other 先定义时,达不到我们预期的目标。

完成UrlRewriter 的定义后, 运行 java org.antlr.Tool UrlRewriter.g,由 Antlr 生成词法分析器 UrlRewriter.java,UrlRewriter.java 就是我们需要的重写器。调用 Antlr 提供的运行时 API,为重写器编写以下测试代码,如清单 8 所示,至此完成了一个完整的重写器的例子。


清单 12. 重写器的测试代码

  public static void main(String[] args) throws Exception {      InputStream in =  new FileInputStream("index.html");     ANTLRInputStream input  = new ANTLRInputStream(in, "UTF-8");     PrintStream out = System.out;     UrlRewriter lexer = new UrlRewriter(input, out); CommonTokenStream tokens = new CommonTokenStream(lexer); for (Object obj : tokens.getTokens());  } 

?

和之前的测试代码相比,创建重写器时我们使用了新的构造函数,用于向重写器传递输出流,实际应用中输出流被赋值为 ServletOutputStream,例子中简单起见直接使用了控制台输出流。重写器的新构造函数是通过定义词法文件时,通过 member 关键字,直接定义在词法文件中的,如清单 13 所示。


清单 13. 为重写器指定输出流

  @members {  private java.io.OutputStream out;  public UrlRewriter(CharStream input, OutputStream out){   this(input);   this.out = out;  }  } 

?

结束语

文本处理是软件开发人员经常面临的工作之一,本文结合开源语言识别工具 Antlr,详细介绍了如何使用 Antlr 开发词法分析器,进而将词法分析器作为 Extractor、Translator 和 Rewriter,进行常规的文本处理。对正则表达式感兴趣的读者,可以使用正则表达式来完成这些工作,并把你做法和本文的做法进行对比,可以进一步发现两者的优劣。

?

参考资料

学习

使用 Antlr 开发领域语言,我的另一篇关于 Antlr 的文章,全面介绍了 Antlr 在领域语言开发方面的各种技术。

Antlr 全球站点, 有关于 Anltr 的最全面的参考资料。

The Definitive ANTLR Reference (Building Domain-Specific Languages): Terence Parr 最新的 Antlr 著作。

Domain Specific Languages (Martin Fowler,Addison-Wesley,2010 年):Fowler 的新书。

developerWorks Java 技术专区:这里有数百篇关于 Java 编程各个方面的文章。

读书人网 >编程

热点推荐