使用 Antlr 处理文本
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Fra Baidu bibliotek
页码,2/5
清单 2. 指定 filter 选项 options{filter=true;}
后续的工作只是根据我们要抽取的文本的特点定义其对应的词法规则。在本例中我们要抽取的SQL文本具有如下形式,如清单 3 所示
清单 3. 抽取目标 INSERT INTO SYSA.IF_EMPUSRRLA(USRNUM,EMPNUM) VALUES('U037508','275159') DB20000I The SQL command completed successfully. INSERT INTO SYSA.IF_USRSTNRLA 根据上述 SQL 的特点,分别定义了 ID, INT, WS, SqlFrg四类记号,这四类记号前均有关键字 fragment,如清单 4 所示。fragment 用于指示 Antlr这些记号只是一个记号片段,其主要作用在 于构造其他记号,被其他记号调用,词法分析器 并不会把它们当成一个完整记号向外传递。四类记号的词法定义显而易见,不做过多的说明。
在使用 Antlr 处理文本时, 我们主要使用词法分析器(Lexer)来完成工作。在处理文本时, 文本本身是一个字符流。Antlr 的词法分析器是一个强大的文本处理工具,它能够把字符流依据词 法规则分解成不同的记号,形成记号流,供后续处理。 文本处理工作在我们的日常工作中非常普遍,最常见的是日志分析,比如对应用服务器控制台输出日志进行分析,对应用自身的系统日志进行分析等等;其他一些时候我们甚至会把配置文件、 源程序文件、XML 文件、HTML 文件等当成文本进行处理
回页首 抽取器 Extractor 介绍 一个抽取器负责从一个文本中把满足特定特征的文本提取出来。一个常见的例子是网络爬虫,爬虫程序需要从一个 HTML 文本中抽取出 URL 用于后续的网络遍历。 一个例子 这里演示例子的是抽取一个数据库 SQL 执行日志,把满足某些特征的 SQL 文本抽取出来。SQL 日志的详细信息请参考附件中的 sql.out 文件。SQL 日志中记载了一批 INSERT 语句的执行结 果。其中一些成功执行,而另一些由于主键冲突的原因执行失败了。我们需要开发一个抽取器,用于从日志中把所有执行成功的 INSERT 语句提取出来。 使用 Antlr 开发抽取器的第一步是,建立词法文件 SqlExtrator.g。 在文件的第一行使用两个 Antlr 的关键字 lexer grammar 声明这是一个词法文件,如清单 1 所示
回页首 词法分析器与正则表达式 正则表达式 正则表达式被认为是文本处理的首选工具,当我们使用正则表示式时,首先定义一个正则表达式,然后和预期文本进行匹配,最终再按照正则表示式中的分组,逐一获取相匹配的数据,然后再 进行下一步的处理(输出、替换等等)。在进行比较复杂一些的问题时,使用正则表达式,整体处理过程比较漫长,有时为了处理一个问题,写出的正则表达式晦涩难懂,很不便于维护。 词法分析器 在 Antlr 中词法分析器使用了和语法分析器相同的技术来构造,对词法记号 Token 的匹配使用了递归下降的策略,使得词法分析器具有处理上下文无关文法的能力,而正则表达式所能处理的文 法只包含正则文法(线性文法),因此词法分析器可以处理很多正则表达式难以处理的问题,比如左括号和右括号的成对匹配等。 此外,在 Antlr 中词法分析器所要匹配的词法记号,通过相互引用的方式进行嵌套和递归定义,比正则表达的书写更直观,更加便于维护。 总的来说,使用 Antlr 词法分析器处理文本和正则表达式相比,处理能力更强大,便于开发和测试,在本文的后续部分中,我们一起来看一下如何使用 Antlr 词法分析器完成抽取、转换、重写 这三类文本处理工作。
清单 7. 转换语义定义
fragment SqlFrg :'INSERT INTO SYSA.' t=ID '(' c1=ID ',' c2=ID ')' WS
'(\'' v1=ID '\',\'' v2=INT '\')' { StringBuffer buffer = new StringBuffer(); buffer.append("DELETE FROM SYSA."); buffer.append(t.getText()); buffer.append(" WHERE "); buffer.append(c1.getText()); buffer.append("='"); buffer.append(v1.getText()); buffer.append("' AND "); buffer.append(c2.getText()); buffer.append("='"); buffer.append(v2.getText()); buffer.append("';"); System.out.println(buffer.toString()); };
'VALUES' \
在定义 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 所示,至此完成了一个完整的转换器的例子。测试代码和抽取器几乎一致,唯一的区别是把抽取器换成了转换器。
清单 5. 抽取器主要词法定义 Sql:SqlFrg {System.out.println($SqlFrg.text);} EOL '
DB20000I The SQL command completed successfully.' EOL; fragment EOL: '\n' | '\r' | '\r\n'; 至此词法SqlExtrator 的定义已经全部完成,在命令行运行 java org.antlr.Tool SqlExtrator.g,即得到我们的目标 SqlExtrator.java,SqlExtrator.java 代表了一个抽取器。调用 Antlr 提供的运行时 API,为抽取器编写以下测试代码,如清单 6 所示,至此完成了一个完整的抽取器的例子。
; } 测试代码中构造了一个 ANTLRInputStream 流,并将它作为抽取器 SqlExtrator 的参数生成抽取器的一个实例,最后用抽取器作为参数构造 CommonTokenStream 记号流,最后一个 for 循环 语句用于从记号流中获取所有的记号,从而完成对整个文本的匹配。这里的 for 循环是一个空语句,因为在抽取器中,我们已经为匹配的文本加入了输出到控制台的动作。
页码,1/5
使用 Antlr 处理文本
简介: Antlr 是一个基于 Java开发的功能强大的语言识别工具,其主要功能原本是用于识别编程语言,但是当我们遇到一些常规的文本处理工作时,使用 Antlr 做这些工作可能比单纯的使用正 则表达式高效、有趣的多。本文将和您一起探讨如何使用 Antlr 完成抽取(Extract)、转换(Translate)和重写(Rewrite)这 3 类常见的文本处理工作。 标记本文! 发布日期: 2011 年 8 月 08 日 级别: 高级 访问情况 219 次浏览 Antlr 和文本处理 在我的另一篇文章《 使用 Antlr 开发领域语言》中对 Antlr 是什么、它能做什么以及如何安装使用都做了说明。今天我们的主要工作是关注如何使用 Antlr 处理文本。 Antlr 是一个语言识别工具,主要用于处理计算机编程语言。用户根据编程语言处理的特点,自定义的上下文无关文法。Antlr 根据这些文法,自动生成词法分析器(Lexer)、语法分析器 (Parser)和树分析器 (Tree Parser)。其中词法分析器的输入是字符流,输出是记号流。语法分析器把记号流作为输入,输出抽象语法树。树分析器遍历整个语法树,输出目标代码。3 类分 析器的设计使用了管道过滤器风格,前一个分析器的输出是后一个的输入,其整体的工作流程如图 1 所示。 图 1. Antlr 工作流程
清单 6. 抽取器的测试代码 public static void main(String[] args) throws Exception {
String filename = "errsql.out"; InputStream in = new FileInputStream(filename); ANTLRInputStream input = new ANTLRInputStream(in); SqlExtrator lexer = new SqlExtrator(input); CommonTokenStream tokens = new CommonTokenStream(lexer); for (Object obj : tokens.getTokens())
清单 1. 定义词法文件 lexer grammar SqlExtrator; 唯一需要注意的是词法的名称必须和文件名称一致,否则Antlr 生成词法分析器时会报错,错误类似于 SqlExtrator.g contains grammar xxx; names must be identical,这里统 一使用 SqlExtrator。 在处理文本时,我们往往只关注与词法记号相匹配的文本,而忽略掉其他文本,这在 Antlr 中通过使用全局语法选项 filter 来达到这个目的。在词法文件中使用 filter=true 即表明忽略掉所有和词 法记号不匹配的文本,如清单 2 所示。此外 filter 选项只适用于 Lexer 文件中,Parser 和 Tree Parser 中不能使用这一个选项。
回页首 转换器 Translator 介绍 转换器将在抽取器的基础上,做更多的工作,除了从文本中匹配预期目标外,转换器的输出直接将匹配的文本转换成另一种形式。 一个例子 在前面抽取器的例子基础上,除了对执行成功的INSERT SQL进行匹配外,我们需要将这些INSERT语句转换成相对应的DELETE语句。比如 INSERT INTO SYSA.IF_USRSTNRLA(USRNUM,STNNUM) VALUES('U037697','00007') 将被转换成
清单 4. 抽取器辅助词法定义 fragment SqlFrg :'INSERT INTO SYSA.' ID '(' ID ',' ID ')' WS 'VALUES' '(\'' ID '\',\'' INT '\')'; fragment WS : (' ' |'\t' |'\r' |'\n' )+ ; fragment INT: '0'..'9' + ; fragment ID : ('a'..'z' |'A'..'Z' |'_' ) ('a'..'z' |'A'..'Z' |'_' |'0'..'9' )*; 抽取器的其他词法定义如清单 5 所示, 我们定义了一个 Sql 词法和一个 EOL 词法。EOL 定义了一个换行符;而 Sql 词法引用了之前定义的 SqlFrg,并在匹配 SqlFrg 之后在词法文件中嵌入 了语义动作 (action),用于向控制台输出匹配的结果。所谓语义动作,在这里就表现为合法的 Java 代码。
页码,3/5
DELETE FROM SYSA.IF_USRSTNRLA WHERE USRNUM='U037698' AND STNNUM='00007';
和抽取器的创建步骤类似,首先创建词法文件 SqlTranslator.g,指定文件的类型为 lexer,开启 filter=true 选项。之后开始定义各个词法记号的匹配规则,由于转换器所要匹配的目标和前面的 抽取器类似,这里对各个词法定义不做过多说明,唯一区别与抽取器的是 SqlFrg 的定义,除了要匹配目标 INSERT SQL 外,需要根据 SQL 的语义做出相应的转换,这些转换主要通过在词法 匹配的过程中嵌入语义动作 (action) 完成。如清单 7 所示。
页码,2/5
清单 2. 指定 filter 选项 options{filter=true;}
后续的工作只是根据我们要抽取的文本的特点定义其对应的词法规则。在本例中我们要抽取的SQL文本具有如下形式,如清单 3 所示
清单 3. 抽取目标 INSERT INTO SYSA.IF_EMPUSRRLA(USRNUM,EMPNUM) VALUES('U037508','275159') DB20000I The SQL command completed successfully. INSERT INTO SYSA.IF_USRSTNRLA 根据上述 SQL 的特点,分别定义了 ID, INT, WS, SqlFrg四类记号,这四类记号前均有关键字 fragment,如清单 4 所示。fragment 用于指示 Antlr这些记号只是一个记号片段,其主要作用在 于构造其他记号,被其他记号调用,词法分析器 并不会把它们当成一个完整记号向外传递。四类记号的词法定义显而易见,不做过多的说明。
在使用 Antlr 处理文本时, 我们主要使用词法分析器(Lexer)来完成工作。在处理文本时, 文本本身是一个字符流。Antlr 的词法分析器是一个强大的文本处理工具,它能够把字符流依据词 法规则分解成不同的记号,形成记号流,供后续处理。 文本处理工作在我们的日常工作中非常普遍,最常见的是日志分析,比如对应用服务器控制台输出日志进行分析,对应用自身的系统日志进行分析等等;其他一些时候我们甚至会把配置文件、 源程序文件、XML 文件、HTML 文件等当成文本进行处理
回页首 抽取器 Extractor 介绍 一个抽取器负责从一个文本中把满足特定特征的文本提取出来。一个常见的例子是网络爬虫,爬虫程序需要从一个 HTML 文本中抽取出 URL 用于后续的网络遍历。 一个例子 这里演示例子的是抽取一个数据库 SQL 执行日志,把满足某些特征的 SQL 文本抽取出来。SQL 日志的详细信息请参考附件中的 sql.out 文件。SQL 日志中记载了一批 INSERT 语句的执行结 果。其中一些成功执行,而另一些由于主键冲突的原因执行失败了。我们需要开发一个抽取器,用于从日志中把所有执行成功的 INSERT 语句提取出来。 使用 Antlr 开发抽取器的第一步是,建立词法文件 SqlExtrator.g。 在文件的第一行使用两个 Antlr 的关键字 lexer grammar 声明这是一个词法文件,如清单 1 所示
回页首 词法分析器与正则表达式 正则表达式 正则表达式被认为是文本处理的首选工具,当我们使用正则表示式时,首先定义一个正则表达式,然后和预期文本进行匹配,最终再按照正则表示式中的分组,逐一获取相匹配的数据,然后再 进行下一步的处理(输出、替换等等)。在进行比较复杂一些的问题时,使用正则表达式,整体处理过程比较漫长,有时为了处理一个问题,写出的正则表达式晦涩难懂,很不便于维护。 词法分析器 在 Antlr 中词法分析器使用了和语法分析器相同的技术来构造,对词法记号 Token 的匹配使用了递归下降的策略,使得词法分析器具有处理上下文无关文法的能力,而正则表达式所能处理的文 法只包含正则文法(线性文法),因此词法分析器可以处理很多正则表达式难以处理的问题,比如左括号和右括号的成对匹配等。 此外,在 Antlr 中词法分析器所要匹配的词法记号,通过相互引用的方式进行嵌套和递归定义,比正则表达的书写更直观,更加便于维护。 总的来说,使用 Antlr 词法分析器处理文本和正则表达式相比,处理能力更强大,便于开发和测试,在本文的后续部分中,我们一起来看一下如何使用 Antlr 词法分析器完成抽取、转换、重写 这三类文本处理工作。
清单 7. 转换语义定义
fragment SqlFrg :'INSERT INTO SYSA.' t=ID '(' c1=ID ',' c2=ID ')' WS
'(\'' v1=ID '\',\'' v2=INT '\')' { StringBuffer buffer = new StringBuffer(); buffer.append("DELETE FROM SYSA."); buffer.append(t.getText()); buffer.append(" WHERE "); buffer.append(c1.getText()); buffer.append("='"); buffer.append(v1.getText()); buffer.append("' AND "); buffer.append(c2.getText()); buffer.append("='"); buffer.append(v2.getText()); buffer.append("';"); System.out.println(buffer.toString()); };
'VALUES' \
在定义 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 所示,至此完成了一个完整的转换器的例子。测试代码和抽取器几乎一致,唯一的区别是把抽取器换成了转换器。
清单 5. 抽取器主要词法定义 Sql:SqlFrg {System.out.println($SqlFrg.text);} EOL '
DB20000I The SQL command completed successfully.' EOL; fragment EOL: '\n' | '\r' | '\r\n'; 至此词法SqlExtrator 的定义已经全部完成,在命令行运行 java org.antlr.Tool SqlExtrator.g,即得到我们的目标 SqlExtrator.java,SqlExtrator.java 代表了一个抽取器。调用 Antlr 提供的运行时 API,为抽取器编写以下测试代码,如清单 6 所示,至此完成了一个完整的抽取器的例子。
; } 测试代码中构造了一个 ANTLRInputStream 流,并将它作为抽取器 SqlExtrator 的参数生成抽取器的一个实例,最后用抽取器作为参数构造 CommonTokenStream 记号流,最后一个 for 循环 语句用于从记号流中获取所有的记号,从而完成对整个文本的匹配。这里的 for 循环是一个空语句,因为在抽取器中,我们已经为匹配的文本加入了输出到控制台的动作。
页码,1/5
使用 Antlr 处理文本
简介: Antlr 是一个基于 Java开发的功能强大的语言识别工具,其主要功能原本是用于识别编程语言,但是当我们遇到一些常规的文本处理工作时,使用 Antlr 做这些工作可能比单纯的使用正 则表达式高效、有趣的多。本文将和您一起探讨如何使用 Antlr 完成抽取(Extract)、转换(Translate)和重写(Rewrite)这 3 类常见的文本处理工作。 标记本文! 发布日期: 2011 年 8 月 08 日 级别: 高级 访问情况 219 次浏览 Antlr 和文本处理 在我的另一篇文章《 使用 Antlr 开发领域语言》中对 Antlr 是什么、它能做什么以及如何安装使用都做了说明。今天我们的主要工作是关注如何使用 Antlr 处理文本。 Antlr 是一个语言识别工具,主要用于处理计算机编程语言。用户根据编程语言处理的特点,自定义的上下文无关文法。Antlr 根据这些文法,自动生成词法分析器(Lexer)、语法分析器 (Parser)和树分析器 (Tree Parser)。其中词法分析器的输入是字符流,输出是记号流。语法分析器把记号流作为输入,输出抽象语法树。树分析器遍历整个语法树,输出目标代码。3 类分 析器的设计使用了管道过滤器风格,前一个分析器的输出是后一个的输入,其整体的工作流程如图 1 所示。 图 1. Antlr 工作流程
清单 6. 抽取器的测试代码 public static void main(String[] args) throws Exception {
String filename = "errsql.out"; InputStream in = new FileInputStream(filename); ANTLRInputStream input = new ANTLRInputStream(in); SqlExtrator lexer = new SqlExtrator(input); CommonTokenStream tokens = new CommonTokenStream(lexer); for (Object obj : tokens.getTokens())
清单 1. 定义词法文件 lexer grammar SqlExtrator; 唯一需要注意的是词法的名称必须和文件名称一致,否则Antlr 生成词法分析器时会报错,错误类似于 SqlExtrator.g contains grammar xxx; names must be identical,这里统 一使用 SqlExtrator。 在处理文本时,我们往往只关注与词法记号相匹配的文本,而忽略掉其他文本,这在 Antlr 中通过使用全局语法选项 filter 来达到这个目的。在词法文件中使用 filter=true 即表明忽略掉所有和词 法记号不匹配的文本,如清单 2 所示。此外 filter 选项只适用于 Lexer 文件中,Parser 和 Tree Parser 中不能使用这一个选项。
回页首 转换器 Translator 介绍 转换器将在抽取器的基础上,做更多的工作,除了从文本中匹配预期目标外,转换器的输出直接将匹配的文本转换成另一种形式。 一个例子 在前面抽取器的例子基础上,除了对执行成功的INSERT SQL进行匹配外,我们需要将这些INSERT语句转换成相对应的DELETE语句。比如 INSERT INTO SYSA.IF_USRSTNRLA(USRNUM,STNNUM) VALUES('U037697','00007') 将被转换成
清单 4. 抽取器辅助词法定义 fragment SqlFrg :'INSERT INTO SYSA.' ID '(' ID ',' ID ')' WS 'VALUES' '(\'' ID '\',\'' INT '\')'; fragment WS : (' ' |'\t' |'\r' |'\n' )+ ; fragment INT: '0'..'9' + ; fragment ID : ('a'..'z' |'A'..'Z' |'_' ) ('a'..'z' |'A'..'Z' |'_' |'0'..'9' )*; 抽取器的其他词法定义如清单 5 所示, 我们定义了一个 Sql 词法和一个 EOL 词法。EOL 定义了一个换行符;而 Sql 词法引用了之前定义的 SqlFrg,并在匹配 SqlFrg 之后在词法文件中嵌入 了语义动作 (action),用于向控制台输出匹配的结果。所谓语义动作,在这里就表现为合法的 Java 代码。
页码,3/5
DELETE FROM SYSA.IF_USRSTNRLA WHERE USRNUM='U037698' AND STNNUM='00007';
和抽取器的创建步骤类似,首先创建词法文件 SqlTranslator.g,指定文件的类型为 lexer,开启 filter=true 选项。之后开始定义各个词法记号的匹配规则,由于转换器所要匹配的目标和前面的 抽取器类似,这里对各个词法定义不做过多说明,唯一区别与抽取器的是 SqlFrg 的定义,除了要匹配目标 INSERT SQL 外,需要根据 SQL 的语义做出相应的转换,这些转换主要通过在词法 匹配的过程中嵌入语义动作 (action) 完成。如清单 7 所示。