设为首页 收藏本站
查看: 820|回复: 0

[经验分享] PERL/LEX/YACC技术实现文本解析--XML解析

[复制链接]

尚未签到

发表于 2015-12-26 10:04:42 | 显示全部楼层 |阅读模式
  继周六的p_enum.pl后,再来一篇说说我用perl做的lex,yacc工具。之前说了,我学习lex和yacc的最初动机是为了做个C语言解释器的SHELL;但后来工作中的实际需要也是制作perl版lex和yacc的一个动机。Perl库里有lex和yacc,我没研究过,想来应该比我做的强大,不过对新手来说,未必能容易入手。
  我的第一个应用场景是做一个xml配置文件的排序。XML是标签标记语言,同一级下,TAG顺序本身是无所谓的;但对于测试工作来说,经常要通过文本比较工作来确定两个配置文件差别。如果没有办法将配置文件内容正确排序,对比一个几十K的配置文件,就会耗费个把钟头。对于有频繁对比内容的测试需要来说,这绝对是无法忍受。
  那期间,我正在研究编译原理,以及lex和yacc,自然萌生了做个xml解析器的想法。有了xml解析器,就能将xml内容按hash、array组合方式在perl里表达成对应的数据结构,而排序也就自然不再是个问题。
  工具及xml示例下载地址:
http://files.cnblogs.com/files/hhao020/perl_zlib_re0.001.rar
  要做xml的解析,首先需要定义lex词法文件xml.lex:



%%prioritized from top to bottom
<!--.*-->  := comment
<\?.*?>    := version
</.*?>       := end
<.*?/ >      := sigton
<.*>         := begin
:= value
  接着,需要定义yacc的语法文件xml.yacc:


DSC0000.gif DSC0001.gif


%yacc%
%%prioritized from bottom to top
xml := version EOF       { Xml_Version }
| version pair EOF  { Xml_VersionPair }
pair := pair pair        { Pair_PairPair }
pair := begin end        { Pair_BeginEnd }
| begin value end       { Pair_BeginValueEnd }
| begin pair end        { Pair_BeginPairEnd }
| begin value pairs end { Pair_BeginValuePairEnd }
| sigton                { Pair_Sigton }
| comment               { Pair_Comment }     
%code%
package xml;
use strict;
use warnings;
sub _XmlAlarmMock
{
print @_;
}
sub _XmlDebugMock
{
my $debugInfo = shift;
#print "$debugInfo\n";
sub _printMock{print @_;};
#&zDebug::DataDump(\&_printMock, \@_);
}
sub _XmlCheckNode
{
my $refNode = shift;
if($refNode->{BEGIN})
{
my $begin = $refNode->{BEGIN}->{TEXT};
my $end  = $refNode->{END}->{TEXT};
printf("##### check node $begin, $end.\n");
$begin =~ /^<([a-zA-Z_0-9]+)/;
my $a = $1;
$end =~ /^<\/([a-zA-Z_0-9]+)/;
my $b = $1;
if($a ne $b)
{
&zDebug::DataDump(\&_XmlAlarmMock, $refNode);
&zDebug::DataDump(\&_XmlAlarmMock, $refNode->{BEGIN});
&zDebug::DataDump(\&_XmlAlarmMock, $refNode->{END});
my $line = $refNode->{BEGIN}->{LINE};
print "\nBEGIN <$a> at LINE [$line] missing END!!!\n";
exit(0);
}
}
=pod  
if($refNode->{VALUE})
{
my $value = $refNode->{VALUE}->{TEXT};
if($value =~ /[<>]/)
{
&zDebug::DataDump(\&_XmlAlarmMock, $refNode);
&zDebug::DataDump(\&_XmlAlarmMock, $refNode->{VALUE});
print "\nVALUE contains <>!!!\n";
exit(0);
}
}
=cut  
}
sub _XmlCheckValue
{
my $refNode = shift;

}
sub Xml_Version
{
my @params = @_;  
&_XmlDebugMock(&zError::FunName().' ['.&zError::FileLine().']', \@_);
my @pair;
my %xml = (VERSION=>$params[0], PAIR=>\@pair);
return \%xml;
}
sub Xml_VersionPair
{
my @params = @_;  
&_XmlDebugMock(&zError::FunName().' ['.&zError::FileLine().']', \@_);
my %xml = (VERSION=>$params[0], PAIR=>$params[1]);
return $params[0];
}
sub Pair_BeginEnd
{
my @params = @_;  
&_XmlDebugMock(&zError::FunName().' ['.&zError::FileLine().']', \@_);
my %node;
$node{BEGIN} = $params[0];
$node{END} = $params[1];
&_XmlCheckNode(\%node);
my @pair = (\%node,);
return \@pair;
}
sub Pair_BeginValueEnd
{
my @params = @_;  
&_XmlDebugMock(&zError::FunName().' ['.&zError::FileLine().']', \@_);
my %node;
$node{BEGIN} = $params[0];
$node{VALUE} = $params[1];
$node{END} = $params[2];
&_XmlCheckNode(\%node);
my @pair = (\%node,);
return \@pair;
}
sub Pair_Sigton
{
my @params = @_;  
&_XmlDebugMock(&zError::FunName().' ['.&zError::FileLine().']', \@_);
my %node;
$node{SIGTON} = $params[0];
my @pair = ($params[0],);
return \@pair;
}
sub Pair_Comment
{
my @params = @_;  
&_XmlDebugMock(&zError::FunName().' ['.&zError::FileLine().']', \@_);
my %node;
$node{COMMENT} = $params[0];
my @pair = (\%node,);
return \@pair;
}
sub Pair_BeginPairEnd
{
my @params = @_;  
&_XmlDebugMock(&zError::FunName().' ['.&zError::FileLine().']', \@_);
my %node;
$node{BEGIN} = $params[0];
$node{PAIR} = $params[1];
$node{END} = $params[2];
&_XmlCheckNode(\%node);
my @pair = (\%node,);
return \@pair;
}
sub Pair_BeginValuePairEnd
{
my @params = @_;  
&_XmlDebugMock(&zError::FunName().' ['.&zError::FileLine().']', \@_);
my %node;
$node{BEGIN} = $params[0];
$node{VALUE} = $params[1];
$node{PAIR} = $params[2];
$node{END} = $params[3];
&_XmlCheckNode(\%node);
my @pair = (\%node,);
return \@pair;
}
sub Pair_PairPair
{
my @params = @_;   
&_XmlDebugMock(&zError::FunName().' ['.&zError::FileLine().']', \@_);
push @{$params[0]}, @{$params[1]};
return $params[0];
}
View Code  最后是应用程序部分p_xml.pl:





#/usr/bin/perl
use strict;
use warnings;
use zFile;
use zTrace;
use zError;
use zDebug;
use zLex;
use zLex;
use zYacc;
sub main
{
my $lex = zLex->New(@ARGV);
$lex->SetupFile('xml.lex');
#$lex->PrintDocLex();
my $yacc = zYacc->New(@ARGV);
$yacc->SetupFile('xml.yacc');  
$yacc->SaveCode('xml.pm');
#$yacc->ImportCode('xml', 'xml');
$yacc->PrintGrammarTree();
$yacc->PrintConflictTree();
my $text = $lex->ParserFile('sample0.xml');
&DataDump(\&TraceDebug, $text);
my @re = $yacc->Compile($text);
&DataDump(undef, \@re);
}
&main();
View Code  样例只做了xml到内存数据结构的解析。
  测试用xml文件sample0.xml:



<?xml version="1.0" encoding="UTF-8"?>
<!--Settings for MSP-->
<Config>
<tag1> value1 </tag1>
< Single Node / >
</Config>
  
  很可惜,当时做的最终版本丢了,只有这个中间版本,对某些细节处理不是很好。YACC在不能做reduce操作时,应该进行shift操作。这个版本当时大概为了解决大文本文件信息摘录问题,新加了冲突预测优化,导致某些时候错误的拒绝shift操作。等过些天有空了,将这个问题修正后再更新。比如,下面这个文件处理会因此失败:



<?xml version="1.0" encoding="UTF-8"?>
<!--Settings for MSP-->
<Config> abc
<tag1> value1 </tag1>
< Single Node / >
</Config>
  
  运行perl p_xml.pl -dstack -dcompile可以看到shift,reduce过程。
  Lex相对比较简单。Yacc在设计时,常常会被移进和归约规则困挠。基本原理很简单,就是不能归约时,即移进。但现实情况下,不同的问题需要的处理过程差别还是蛮大。这也是的我做的Lex和Yacc多次改动,也就带来了bug,待有机会好好整理下。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-156448-1-1.html 上篇帖子: Perl Tk摸索 下篇帖子: 用perl写Mysql数据库时内容为乱码
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表