/**********************************************************************
**程序名称:XML语言解析器
**程序功能:完成了对基础的XML语言的解析功能,并提供了一些查询函数
**作者: BUG_lauo
**完成日期:2009-6-8
***********************************************************************/
#include<iostream>
#include"xml.h"
using namespace std;
int main()
{
Xml xml;
//xml.Xml_Analysis(string("<?xml version='1.0' ?><body body='adfasd '> 125 dfasd 8888 <data ad=/"ajfhsd/" >sadjfhas</data> /
<time id='year'> hello <date> </date> <month></month> </time></body>"));
xml.Xml_Analysis("a.xml");
if(xml.Out_Error_Num()!=0)
{
cout<<"Xml解析过程中发现有"<<xml.Out_Error_Num()<<"个错误:"<<endl;
xml.Out_Error_To_Cmd();
}
xml.Out_To_Cmd();
xml.Out_To_File("xml.txt");
/以下将是查询测试
int a=Xml::BROTHER_ALL;
if(xml.Xml_Query("date",Xml::CHILD_ALL))
{
cout<<xml.get_cursor()->name<<" value:"+xml.get_cursor()->value<<endl;;
if(xml.moveto(Xml::CHILD_ALL,1))
cout<<"ok!"<<endl;
xml.begin();
cout<<xml.get_cursor()->name<<" value:"+xml.get_cursor()->value<<endl;;
if(xml.moveto(Xml::CHILD_ALL,0))
cout<<xml.get_cursor()->name<<" value:"+xml.get_cursor()->value<<endl;
}
xml.begin();//将光标移为根目录下
if(xml.Xml_Query("time",Xml::CHILD_ALL))
cout<<xml.get_cursor()->name<<" value:"+xml.get_cursor()->value<<endl;
//system("notepad.exe xml.txt");
return 1;
}
/**********************************************************************
**程序名称:XML语言解析器
**程序功能:完成了对基础的XML语言的解析功能,并提供了一些查询函数
**作者: BUG_lauo
**完成日期:2009-6-8
***********************************************************************/
/***********************************************************************
**文件名: xml.h
**文件内容:定义了Xml语言解析类(包含查询功能)及其所需的基本数据结构
************************************************************************/
#include<iostream>
#include<string>
#include<list>
#include<stack>
using namespace std;
struct xml_attribute//标签属性结构体
{
string attri_name;//属性名
string attri_value;//属性值
};
struct xml_node
{
string name;//标签名称
list<xml_attribute> attribute;//标签对应的属性列表
string value; //用字符串来保存其值
xml_node* brother,*child;//儿子节点及兄弟结点
xml_node():brother(0),child(0){}
~xml_node(){if(brother) delete brother;if(child) delete child;}//析构函数
};
//Xml解析类,从一个字符串中解析得到对应的内容,并形成一个树的结构
//并且提供查询定位功能
class Xml
{
private:
xml_node* root;//Xml树的根结点
xml_node* cursor;//用于定位用的“光标”
string xml_str;
int current;//解析时正在分析的字符
int err_num;//解析的错误数
list<string> ErrorList;//解析出错暂存列表
stack<string*> value_stack;//标签名对应的值,压栈以便在中间时仍然可对其正确赋值
public:
enum Direct{CHILD_ONLY,CHILD_ALL,BROTHER_ONLY,BROTHER_ALL};//遍历的方向
private:///Xml解析常用内部服务函数
bool moveto(char ch);//移动到对应的字符处,返回值为空,表明已经到达xml_str尾
bool filter();//将空格过滤掉,找到最起始的非空字符,如果返回值为空,表明已经到达宽以xml_str的结束处
bool get_name(string &str);//得到一个标识符名字
bool get_value(string &value);//得到标签的值
bool get_attri_value(string &value);//得到属性值,如果出错则返回
bool is_id_letter(char ch);//是合法的标签名组成字符,包括字符、数字和下划线
bool is_space_char(char ch);//是否是类空格的字符
bool error(string err_str);//出错处理函数,返回值必为
string error_skim_str(int len);//出错时对出错位图提供一个查看的缩略的字串
string SpaceFormat(const string str,int level,int len=10);//允许的显示的字符串最大长度为个字符
void Out_To_Cmd(xml_node* ptr,int level);//用于递归输出
void Out_To_File(FILE* file,xml_node* ptr,int level);//解析结果(XML树)输出到文件
private:///Xml解析功能函数
bool Xml_head();//Xml文件头
bool Xml_body(xml_node*&);//Xml语法单元:头标签、尾标签及其间的部分
xml_node* front_label();//头标签
bool rear_label(string label_name);//结尾标签
bool get_attribute(xml_node* ptr);//得到标签的属性
public:/Xml解析常用接口函数
Xml(string str=""):xml_str(str),root(0),current(0),
err_num(0),cursor(0){};
~Xml(){if(root) delete root;}
bool Xml_Analysis();
bool Xml_Analysis(string str);//对str进行分析,得到Xml树
bool Xml_Analysis(const char* filename);//对一个xml文件进行分析,到得相应的内容
int Out_Error_Num(){return err_num;}
void Out_Error_To_Cmd();
void Out_To_Cmd();//输出主程序
bool Out_To_File(FILE* file);//解析得到的结果(XML树)输出到屏幕
bool Out_To_File(const char* filename);//解析得到的结果(XML树)输出到文件
public:/查询部分对外接口函数
bool Xml_Query(string key,Direct flag);//flag标识是往什么方向遍历的,使用了enum Direct
void begin(){cursor=root;}//将光标移回到根结点下
xml_node* get_cursor(){return cursor;}//得到光标所在位置
bool moveto(Direct flag,int n);//向儿子树杈移动n步,如果移动失败,则返回为,并且保持当前值不变
private:///Xml查询内部常用服务函数
bool SearchBrotherFirst(string key,Direct flag);//儿子优先搜索
bool SearchChildFirst(string key,Direct flag); //兄弟优先搜索
};
/**********************************************************************
**程序名称:XML语言解析器
**程序功能:完成了对基础的XML语言的解析功能,并提供了一些查询函数
**作者: BUG_lauo
**完成日期:2009-6-8
***********************************************************************/
/***********************************************************************
**文件名: xml.cpp
**文件内容:实现xml.h里定义的Xml类的所有成员函数,包括解析函数及查询函数
************************************************************************/
#include"xml.h"
/********************Xml解析功能所需的内部服务函数**********************/
bool Xml::moveto(char ch)//移动到指定的字符处
{
int mlen=xml_str.length();
while(current!=mlen)
{
if(xml_str[current]==ch)
break;
current++;
}
if(current==mlen)//已经到达xml_str尾
return 0;
return 1;
}
bool Xml::filter()//类空格字符过滤
{
int mlen=xml_str.length();
while(current<mlen && is_space_char(xml_str[current]))//将类空格的字符过滤掉
current++;
return current<mlen;
}
bool Xml::get_name(string &str)//得到一个标识符名字
{
int mlen=xml_str.length();
while(current<mlen&& is_id_letter( xml_str[current]))
str+=xml_str[current++];//先赋值后自增
if(str.length()==0||str[0]<='9'&&str[0]>='0')//如果得到了空名或者首字符为数字,则返回为
return 0;
return 1;
}
bool Xml::get_value(string &value)//得到标签的值
{
int mlen=xml_str.length();
while(current<mlen && xml_str[current]!='<')
{
if(is_space_char(xml_str[current])&&filter())
value+=' ';
else if(current<mlen)
value+=xml_str[current++];
}
return current<mlen;
}
bool Xml::get_attri_value(string &value)//得到属性值,如果出错则返回
{
int mlen=xml_str.length();
if(xml_str[current]=='/'')
{
current++;
while(current<mlen&&xml_str[current]!='/'')
value+=xml_str[current++];
current++;
return current<mlen;//如果未到达xml_str尾,则表明属性正确
}
else if(xml_str[current]=='"')
{
current++;
while(current<mlen&&xml_str[current]!='"')
value+=xml_str[current++];
current++;
return current<mlen;//如果未到达xml_str尾,则表明属性正确
}
else
return 0;//属性出错
}
inline bool Xml::is_id_letter(char ch)//是合法的标签名组成字符,包括字符、数字和下划线
{
return (ch>='0'&& ch<='9'|| ch>='a'&& ch<='z'|| ch=='_' ||
ch>='A'&& ch<='Z');
}
inline bool Xml::is_space_char(char ch)//是否是类空格的字符
{
if(ch==' '||ch=='/n'||ch=='/r'||ch=='/t'||ch==EOF)
return 1;
return 0;
}
inline bool Xml::error(string err_str)
{
err_num++;
char buf[256];
itoa(err_num,buf,10);
ErrorList.insert(ErrorList.end(),string("Error ")+buf+":"+err_str+" At: "+error_skim_str(15));
return 0;
}
string Xml::error_skim_str(int len)//出错时对出错位图提供一个查看的缩略的字串
{
string str;
int begin=(current - len/2 >=0 ) ? current - len/2:0,
mlen=xml_str.length();
for(;begin<len&&begin<mlen;begin++)
str+=xml_str[begin];
return str;
}
/******************************************************************************/
/**************************Xml语言解析类输出函数如下***************************/
void Xml::Out_Error_To_Cmd()
{
for(list<string>::iterator i=ErrorList.begin();i!=ErrorList.end();i++)
cout<<*i<<endl;
}
string Xml::SpaceFormat(const string str,int level,int len)//允许的显示的字符串最大长度为个字符
{
string space,encode;
for(int i=0;i<len;space+=' ',i++);//得到标准空格的长度
space[0]='.';
for(int i=0;i<level;i++) encode+=space;//得到在特定层数下的标准空格行
for(int i=0,len=str.length();i<10&&i<len;i++) encode+=str[i];//得到要输出的字串
return encode;
}
void Xml::Out_To_Cmd()
{
if(root)
Out_To_Cmd(root,0);
}
void Xml::Out_To_Cmd(xml_node* ptr,int level)//用于递归输出
{
cout<<SpaceFormat(ptr->name,level)<<(ptr->value.length()!=0?" value:"+ptr->value:"");
list<xml_attribute>::iterator i=ptr->attribute.begin();
if(i!=ptr->attribute.end())
{
cout<<"( ";
for(;i!=ptr->attribute.end();i++)
cout<<i->attri_name+"=/""+i->attri_value+"/" ";
cout<<")";//换行
}
cout<<"/n";
if(ptr->child)//如果儿子节点存在
Out_To_Cmd(ptr->child,level+1);//不换行
if(ptr->brother)//如果brother结点存在
Out_To_Cmd(ptr->brother,level);//换行
}
void Xml::Out_To_File(FILE* file,xml_node *ptr,int level)
{
fprintf(file,"%s",(SpaceFormat(ptr->name,level)+(ptr->value.length()!=0?" value:"+ptr->value:"")).c_str());
list<xml_attribute>::iterator i=ptr->attribute.begin();
if(i!=ptr->attribute.end())
{
fprintf(file,"( ");
for(;i!=ptr->attribute.end();i++)
fprintf(file,"%s",(i->attri_name+"=/""+i->attri_value+"/" ").c_str());
fprintf(file,")");//换行
}
fprintf(file,"/n");
if(ptr->child)//如果儿子节点存在
Out_To_File(file,ptr->child,level+1);//不换行
if(ptr->brother)//如果brother结点存在
Out_To_File(file,ptr->brother,level);//换行
}
bool Xml::Out_To_File(FILE* file)
{
if(root) Out_To_File(file,root,0);
else return 0;
return 1;
}
bool Xml::Out_To_File(const char* filename)
{
FILE* file=fopen(filename,"w");
if(file)
{
if(Out_To_File(file))//假如分析成功
{
fclose(file);
return 1;
}
else
{
fclose(file);
return 0;
}
}
return 0;
}
/**********************************************************************/
/*******************Xml解析类解析功能函数如下*************************/
bool Xml::Xml_Analysis(string str)//对str进行分析,得到Xml树
{
if(str=="")//要分析的字串为空
return 0;
xml_str=str;
return Xml_Analysis();
}
bool Xml::Xml_Analysis()//对xml_str进行分析
{
this->err_num=0;//错误数置为零
ErrorList.clear();//将错误列表清空
if(xml_str=="")//要分析的字串为空
return 0;
if(Xml_head())//如何Xml头正确,则继续分析body
return Xml_body(root);
return 0;//Xml头出错,返回值为
}
bool Xml::Xml_Analysis(const char* filename)//对一个xml文件进行分析,到得相应的内容
{
FILE* file=fopen(filename,"r");
if(file==0)
return 0;
char buf[256]={0};
while(fread(buf,1,256,file))
{
xml_str+=buf;
memset(buf,0,256);
}
fclose(file);
return Xml_Analysis();
}
bool Xml::Xml_head()
{
if(moveto('<'))
{
if(xml_str.length()-current>5)//当字符数不够个的时候,显然不全,故出错
{
if(!(xml_str[++current]=='?'&&xml_str[++current]=='x'&&
xml_str[++current]=='m'&&xml_str[++current]=='l'&&moveto('?')&&xml_str[++current]=='>'))
return error("Xml头出错!");
}
else//字符长度不足例如"<?xml"
return error("Xml头出错!");
}
return 1;
}
bool Xml::Xml_body(xml_node*& ptr)
{
ptr=NULL;
if(moveto('<'))//已经内含了filter()功能
{
ptr=front_label();//得到了首标
if(ptr&&filter())//正确获取标签,且有可能找到结束标签
{
value_stack.push(&(ptr->value));//将要找到的value压栈,以便中途赋值
if(xml_str[current]!='<')//得到其值
{
if(!get_value(ptr->value))
return error("Xml意外结束!");
}
if(moveto('<')&&xml_str[current+1]!='/')//如果标签未结束,则表明为新的标签要开始!
if(!Xml_body(ptr->child))
return error("Xml子标签出错!");
if(moveto('<')&&xml_str[current+1]=='/')//如果是结束标签
{
current+=2;
if(!rear_label(ptr->name))
return error("标签"+ptr->name+"未配对!");
moveto('>');
current++;
}
}
else
return error("Xml未包含体部分!");
}
value_stack.pop();
if(filter())
{
if(xml_str[current]!='<')
{
if(value_stack.empty())
return error("意外的标签值出现!");
string* value=value_stack.top();
if(value&&!get_value(*value))
return error("Xml获取标签值时意外结束!");
}
if(filter()&&xml_str[current]=='<'&&xml_str[current+1]!='/')//如果不是结束标签,而是开始标签
{
if(root==ptr)
return error("Xml体只允许出现一个根标签!");
if(is_space_char(xml_str[current+1]))
return error("Xml标签'<'后首字符为非法字符!");
if(!Xml_body(ptr->brother))
return 0;//生成brother部分
}
}
return 1;
}
xml_node* Xml::front_label()
{
xml_node* ptr=new xml_node;
current++;//原来指向'<',故移到下一个字符,此时name开始
if(!is_id_letter(xml_str[current]))
return (xml_node*)error("'<'与标签名之间包含有非法字符!");
if(get_name(ptr->name))
{
if(filter())
{
if(xml_str[current]!='>'&&!get_attribute(ptr))
return (xml_node*)error("错误的属性值!");
if(filter() && xml_str[current]=='>')//已经标签结束
current++;//指向下一个未处理字符
return ptr;//将得到的标签返回
}
else
return (xml_node*)error("标签意外中止(标签不完整)!");
}
else
{
delete ptr;//释放已经申请的空间
return (xml_node*)error("未包含标签名或者标签名非法!");
}
}
bool Xml::get_attribute(xml_node* ptr)
{
string name;
int mlen=xml_str.length();
while(current<mlen && xml_str[current]!='>')
{
if(filter() && is_id_letter(xml_str[current]) && get_name(name))
{
xml_attribute attri;
attri.attri_name=name;//属性名
if(filter()&&xml_str[current++]=='='&&filter())
if(!get_attri_value(attri.attri_value))//得到了正确的属性值
return error("错误的属性值!");
ptr->attribute.insert(ptr->attribute.end(),attri);
}
else
return error("属性名错误!");
filter();//注意这里最后将其移动到下一个未为类空字符,以保证能正确判断
}
return 1;
}
bool Xml::rear_label(string name)
{
string id;
if(!get_name(id))
return 0;
if(id!=name)
return 0;
if(moveto('>'))
return 1;
return 0;
}
/********************************************************************************/
/*******************************Xml查询函数如下**********************************/
bool Xml::Xml_Query(string key,Direct flag)
{
if(cursor==NULL)//如果cursor为,表明空置了光标,应将其置为root
cursor=root;
if(root==NULL)//树根为空,返回
return 0;
xml_node* ptr=cursor;//如果查询失败,应将光标恢复到此处
bool bflag=0;
if(flag==CHILD_ONLY||flag==CHILD_ALL)
bflag=SearchChildFirst(key,flag);
else
bflag=SearchBrotherFirst(key,flag);
cursor=bflag?cursor:ptr;//如果查询失败,则cursor恢复为原值
return bflag;
}
bool Xml::SearchChildFirst(string key,Direct flag)
{
if(cursor==NULL)
return 0;
xml_node* ptr;
if(cursor->name==key)//找到,返回为
return 1;
else
{
ptr=cursor->brother;
cursor=cursor->child;
if(SearchChildFirst(key,flag))//从儿子结点开始继续查找
return 1;
if(flag==CHILD_ALL)
{
cursor=ptr;//向兄弟结点方向继续找
return SearchChildFirst(key,flag);
}
return 0;
}
}
bool Xml::SearchBrotherFirst(string key,Direct flag)
{
xml_node* ptr;
if(cursor->name==key)//找到,返回为
return 1;
else
{
ptr=cursor->child;
cursor=cursor->brother;
if(SearchBrotherFirst(key,flag))//从兄弟结点开始继续查找
return 1;
if(flag==BROTHER_ALL)//向儿子结点方向继续找
{
cursor=ptr;
return SearchBrotherFirst(key,flag);
}
return 0;
}
}
bool Xml::moveto(Direct flag,int n)//向儿子树杈移动n步,如果移动失败,则返回为,并且保持当前值不变
{
if(cursor==NULL)//如果当前光标已经指向空,则让它从根结点开始
cursor=root;
xml_node* ptr=cursor;//以便恢复当前值
if(flag==CHILD_ALL||flag==CHILD_ONLY)
while(n>0&&cursor!=NULL)
cursor=cursor->child,n--;
else
while(n>0&&cursor!=NULL)
cursor=cursor->child,n--;
if(cursor==NULL)//表明移动失败
{
cursor=ptr;
return 0;
}
return 1;//移动成功
}