游戏开发论坛

 找回密码
 立即注册
搜索
查看: 7718|回复: 22

我们写的一个小巧的XML解析类,感兴趣的可以用用。

[复制链接]

30

主题

569

帖子

569

积分

高级会员

Rank: 4

积分
569
发表于 2006-6-13 05:54:00 | 显示全部楼层 |阅读模式
//xml.h的内容。

#pragma once

#include <list>
#include <assert.h>
#include <vector>


//
class XmlNode;
bool Parse( char*& sXmlBuffer, XmlNode& node );
bool ParseAttrib( char*& sXmlBuffer, XmlNode& node );
bool ParseChild( char*& sXmlBuffer, XmlNode& node );

/*
Xml节点属性。
*/
typedef struct
{
char* m_sAttribNameBegin; // 属性名字。
mutable char* m_sAttribNameEnd;
char* m_sAttribDataBegin; // 属性数据。
mutable char* m_sAttribDataEnd;
}XmlAttrib;

/*
XML节点,包括名字,属性和文本三种信息。
此外还包含子节点,父节点的信息。
*/
class XmlNode
{
char* m_sNameBegin; // 节点名字。
mutable char* m_sNameEnd;

std::vector< XmlAttrib > m_Attribs; // 所有的属性。

char* m_sDataBegin; // 节点数据。
mutable char* m_sDataEnd;

XmlNode* m_Pater; // 父节点。
std::vector< XmlNode > m_Childs; // 所有的子节点。

friend bool Parse( char*& sXmlBuffer, XmlNode& node ); // 解析。
friend bool ParseAttrib( char*& sXmlBuffer, XmlNode& node ); // 解析属性。
friend bool ParseChild( char*& sXmlBuffer, XmlNode& node ); // 解析子节点。

public:

// 返回节点的名字。
const char* GetName( ) const;
// 返回节点的文本。
const char* GetData() const;
// 获取属性总数。
long GetAttribCount() const;
// 获取属性名字。
const char* GetAttribName( long id ) const;
// 获取属性。
const char* GetAttrib( long id ) const;
// 查询属性。
const char* QueryAttrib( const char* name ) const;
// 获取子节点总数。
long GetChildNodeCount() const;
// 获取父节点。
const XmlNode* GetPater() const;
// 获取子节点。
const XmlNode* GetChildNode( long id ) const;
// 获取本节点的次序。
long GetNodeId() const;
// 获取前一个节点。
const XmlNode* GetPrevNode() const;
// 获取后一个节点。
const XmlNode* GetNextNode() const;
// 查询子节点。
const XmlNode* QueryChild( const char* name ) const;

public:

XmlNode( XmlNode* pater );
~XmlNode();
};

/*
*/
class XmlDocument
{
//
char* m_sBuffer; // 缓冲区。
XmlNode* m_XmlNodeRoot; // 引导节点。


public:
/*
构造。
*/
XmlDocument(void);
/*
析构。
*/
~XmlDocument(void);

/*
说明 打开XML文挡并解析。
返回 打开是否成功。
参数 为XML文件名字。
*/
bool Open( const char* sFileName );

/*
说明 关闭XML文挡,释放内存。
*/
void Close( );

/*
说明 获取引导节点。
返回 引导节点。
*/
const XmlNode* GetRoot();

};

30

主题

569

帖子

569

积分

高级会员

Rank: 4

积分
569
 楼主| 发表于 2006-6-13 05:56:00 | 显示全部楼层

Re:我们写的一个小巧的XML解析类,感兴趣的可以用用。

#include "StdAfx.h"
#include ".\xml.h"


/*
Xml单词的属性。
*/
enum XmlWordAttrib
{
XmlLeft = 1, // "<",不能是"</"
XmlLeftOver = 1<<1, // "</"
XmlRight = 1<<2, // ">"
XmlRightOver= 1<<3, // "/>"
XmlName = 1<<4, // 节点名字,必须为合法的字符。
XmlSymbol = 1<<5, // 节点属性名字,必须合法的字符,不能小于32,不能是'<','>','"','='
XmlData = 1<<6, // 节点数据,非'<','>'的任意字符。
XmlString = 1<<7, // 节点属性数据。'"'。
XmlEqual = 1<<8, // 等号。'='。
XmlError = 1<<9 // 返回此标记表示语法错。
};

// 回滚操作。
class XmlBackroll
{
char* m_sBackrollMark;
char*& m_sBackroll;
public:
XmlBackroll( char*& sXmlBuffer )
: m_sBackroll ( sXmlBuffer ),
m_sBackrollMark ( sXmlBuffer )
{
};
~XmlBackroll()
{
m_sBackroll = m_sBackrollMark;
}
};

// 声明回滚。
#define DECLARE_XML_BACKROLL XmlBackroll backroll( sXmlBuffer );
// 回滚数据。
#define XML_BACKROLL sXmlBuffer = sBackroll_;
// 移动到下一个字符。
#define XML_NEXT ++ sXmlBuffer; if( !*sXmlBuffer ) return false;
// 移动到前一个字符。
#define XML_PREV --sXmlBuffer;

// 移动到指定的条件下。
#define XML_TO( compare_ ) while( 1 ) { if( !*sXmlBuffer ) return false; if( compare_ ) break; ++ sXmlBuffer; }

// "<",不能是"</"。
bool IsXmlLeft( char*& sXmlBuffer )
{
// 定义回滚。
DECLARE_XML_BACKROLL;

//
if( *sXmlBuffer == '<' )
{
XML_NEXT;
return *sXmlBuffer != '/';
}

// 返回失败。
return false;
}

// 到"<"结束,移动一字符。
bool OverXmlLeft( char*& sXmlBuffer )
{
XML_NEXT;
return true;
};

// "</"。
bool IsXmlLeftOver( char*& sXmlBuffer )
{
//
DECLARE_XML_BACKROLL;

//
if( *sXmlBuffer == '<' )
{
XML_NEXT;
return *sXmlBuffer == '/';
}

//
return false;
}

// 到"</"结束,移动两字符。
bool OverXmlLeftOver( char*& sXmlBuffer )
{
XML_NEXT;
XML_NEXT;
return true;
}

// ">"。
bool IsXmlRight( char*& sXmlBuffer )
{
return *sXmlBuffer == '>';
}

// 到">"结束,移动一字符。
bool OverXmlRight( char*& sXmlBuffer )
{
XML_NEXT;
return true;
}

// "/>"
bool IsXmlRightOver( char*& sXmlBuffer )
{
//
DECLARE_XML_BACKROLL;

//
if( *sXmlBuffer == '/' )
{
XML_NEXT;
return *sXmlBuffer == '>';
}

//
return false;
}

// 到"/>"结束,移动两个字节。
bool OverXmlRightOver( char*& sXmlBuffer )
{
XML_NEXT;
XML_NEXT;
return true;
}

// 节点名字,合法字符,不能是'=','<','>','/'。
bool IsXmlName( char*& sXmlBuffer )
{
return *sXmlBuffer > 32 && *sXmlBuffer != '=' && *sXmlBuffer != '<' && *sXmlBuffer != '>' && *sXmlBuffer != '/';
};

// 到节点名字结束。
bool OverXmlName( char*& sXmlBuffer )
{
XML_TO( *sXmlBuffer<=32||*sXmlBuffer=='>'||*sXmlBuffer=='/' );
return true;
};

// 节点属性名字,>32,不能是'<','>','"','='
bool IsXmlSymbol( char*& sXmlBuffer )
{
return *sXmlBuffer > 32 && *sXmlBuffer != '=' && *sXmlBuffer != '<' && *sXmlBuffer != '>' && *sXmlBuffer != '/';
}

// 到节点属性名字结束。
bool OverXmlSymbol( char*& sXmlBuffer )
{
XML_TO( *sXmlBuffer<=32||*sXmlBuffer=='=' );
return true;
}

// 数据 >32,非'<','>'。
bool IsXmlData( char*& sXmlBuffer )
{
return *sXmlBuffer > 32 && *sXmlBuffer != '<' && *sXmlBuffer != '>';
}

// 到数据结束。
bool OverXmlData( char*& sXmlBuffer )
{
XML_TO( *sXmlBuffer=='<'&&sXmlBuffer[1]=='/' );
return true;
}

// 属性。'"',成功移动到'"'的下一个字符。
// ok!
bool IsXmlString( char*& sXmlBuffer )
{
return *sXmlBuffer == '"';
}

// 到属性结束。
bool OverXmlString( char*& sXmlBuffer )
{
XML_NEXT;
XML_TO( *sXmlBuffer=='"' );
XML_NEXT;
return true;
}

// 等号。'='。
bool IsXmlEqual( char*& sXmlBuffer )
{
return *sXmlBuffer=='=';
}

// 到等号结束。
bool OverXmlEqual( char*& sXmlBuffer )
{
XML_NEXT;
return true;
}

/*
获取下一个符号的开始位置。
*/
XmlWordAttrib GetToken( char*& sXmlBuffer, unsigned int flage )
{
// 遍历每个字符,如果到达字符串缓冲区结束位置,则返回失败。
while( *sXmlBuffer )
{
// 查找无效字符。
if( *sXmlBuffer > 32 )
{
if( flage & XmlData && IsXmlData( sXmlBuffer ) )
return XmlData;

if( flage & XmlLeft && IsXmlLeft( sXmlBuffer ) )
return XmlLeft;

if( flage & XmlLeftOver && IsXmlLeftOver( sXmlBuffer ) )
return XmlLeftOver;

if( flage & XmlRight && IsXmlRight( sXmlBuffer ) )
return XmlRight;

if( flage & XmlRightOver&& IsXmlRightOver( sXmlBuffer ) )
return XmlRightOver;

if( flage & XmlName && IsXmlName( sXmlBuffer ) )
return XmlName;

if( flage & XmlSymbol && IsXmlSymbol( sXmlBuffer ) )
return XmlSymbol;

if( flage & XmlString && IsXmlString( sXmlBuffer ) )
return XmlString;

if( flage & XmlEqual && IsXmlEqual( sXmlBuffer ) )
return XmlEqual;

return XmlError;
}

++ sXmlBuffer;
}

// 返回失败。
return XmlError;
};

// 解析属性,进入时缓冲区位于属性名字的第一个字符。
bool ParseAttrib( char*& sXmlBuffer, XmlNode& node )
{
//
XmlAttrib attrib;

// 解析符号。
if( XmlSymbol != GetToken( sXmlBuffer, XmlSymbol ) )
return false;
attrib.m_sAttribNameBegin = sXmlBuffer;

if( !OverXmlSymbol( sXmlBuffer ) )
return false;
attrib.m_sAttribNameEnd = sXmlBuffer;

// 解析等号。
if( XmlEqual != GetToken( sXmlBuffer, XmlEqual ) )
return false;

OverXmlEqual( sXmlBuffer );

// 解析串。
if( XmlString != GetToken( sXmlBuffer, XmlString ) )
return false;
attrib.m_sAttribDataBegin = ++ sXmlBuffer;

if( !OverXmlString( sXmlBuffer ) )
return false;
attrib.m_sAttribDataEnd = sXmlBuffer - 1;

// 添加属性。
node.m_Attribs.push_back( attrib );

// 如果接下来仍然是符号,则递归。
if( XmlSymbol == GetToken( sXmlBuffer, XmlSymbol|XmlRight|XmlRightOver ) )
ParseAttrib( sXmlBuffer, node );

// 返回成功。
return true;
};

// 解析节点。
bool Parse( char*& sXmlBuffer, XmlNode& node )
{
// 解析名字。
if( XmlLeft != GetToken( sXmlBuffer, XmlLeft ) // 检查'<'和跳过'<'
|| !OverXmlLeft( sXmlBuffer )
)
{
GAMEWARNING( "get node name < error!" );
return false;
}

if( XmlName != GetToken( sXmlBuffer, XmlName ) ) // 检查名字。
{
GAMEWARNING( "get node name error!" );
return false;
}
node.m_sNameBegin = sXmlBuffer;

if( !OverXmlName( sXmlBuffer ) ) // 跳过名字。
{
GAMEWARNING( "over node name error!" );
return false;
}
node.m_sNameEnd = sXmlBuffer;

// 解析属性。
if( XmlSymbol == GetToken( sXmlBuffer, XmlSymbol|XmlRight|XmlRightOver ) )
ParseAttrib( sXmlBuffer, node );

// 解析'/>'。
if( XmlRightOver == GetToken( sXmlBuffer, XmlRight|XmlRightOver ) )
{
if( !OverXmlRightOver( sXmlBuffer ) )
{
GAMEWARNING( "over xml node /> error!" );
return false;
}
return true;
}

// 解析'>'。
if( XmlRight == GetToken( sXmlBuffer, XmlRight )
&& !OverXmlRight( sXmlBuffer )
)
{
GAMEWARNING( "get node name > error!" );
return false;
}

// 解析数据。
if( XmlData == GetToken( sXmlBuffer, XmlData|XmlLeft|XmlLeftOver ) )
{
node.m_sDataBegin = sXmlBuffer;

if( !OverXmlData( sXmlBuffer ) )
{
GAMEWARNING( "over node data error!" );
return false;
}

node.m_sDataEnd = sXmlBuffer;
}

// 解析子节点。
while( XmlLeft == GetToken( sXmlBuffer, XmlLeft|XmlLeftOver ) )
{
XmlNode child( &node );

if( !Parse( sXmlBuffer, child ) )
{
GAMEWARNING( "parse %s child node error!", node.GetName() );
return false;
}

node.m_Childs.push_back( child );
}

// 解析关闭节点。
if( XmlLeftOver != GetToken( sXmlBuffer, XmlLeftOver )
|| !OverXmlLeftOver( sXmlBuffer )
)
{
GAMEWARNING( "get or over </ error!" );
return false;
}

if( XmlName != GetToken( sXmlBuffer, XmlName ) )
{
GAMEWARNING( "get over name error!" );
return false;
}

char* name = sXmlBuffer;

if( !OverXmlName( sXmlBuffer ) )
{
GAMEWARNING( "over over name error!" );
return false;
}

char temp = *sXmlBuffer;
*sXmlBuffer = 0;

*node.m_sNameEnd = 0;
if( strcmp( name, node.m_sNameBegin ) )
{
GAMEWARNING( "over name not equal node name!" );
return false;
}

*sXmlBuffer = temp;

if( XmlRight != GetToken( sXmlBuffer, XmlRight )
|| !OverXmlRight( sXmlBuffer )
)
{
GAMEWARNING( "over name > error!" );
return false;
}

// 解析成功。
return true;
};


// 返回节点的名字。
const char* XmlNode::GetName( ) const
{
if( m_sNameEnd )
*m_sNameEnd = 0;

return m_sNameBegin;
}

// 返回节点的文本。
const char* XmlNode::GetData() const
{
if( m_sDataEnd )
*m_sDataEnd = 0;

return m_sDataBegin;
}

// 获取属性总数。
long XmlNode::GetAttribCount() const
{
return (long)m_Attribs.size();
};

// 获取属性名字。
const char* XmlNode::GetAttribName( long id ) const
{
if( id < 0 || id >= GetAttribCount() )
return NULL;

if( m_Attribs[id].m_sAttribNameEnd )
*m_Attribs[id].m_sAttribNameEnd = 0;

return m_Attribs[id].m_sAttribNameBegin;
};

// 获取属性。
const char* XmlNode::GetAttrib( long id ) const
{
if( id < 0 || id >= GetAttribCount() )
return NULL;

if( m_Attribs[id].m_sAttribDataEnd )
*m_Attribs[id].m_sAttribDataEnd = 0;

return m_Attribs[id].m_sAttribDataBegin;
};

// 查询属性。
const char* XmlNode:ueryAttrib( const char* name ) const
{
for( unsigned int i = 0; i < m_Attribs.size(); i ++ )
{
*m_Attribs.m_sAttribNameEnd = 0;

#ifdef _DEBUG
const char* c = m_Attribs.m_sAttribNameBegin;
#endif

if( !strcmp( m_Attribs.m_sAttribNameBegin, name ) )
{
*m_Attribs.m_sAttribDataEnd = 0;
return m_Attribs.m_sAttribDataBegin;
}
}

return NULL;
};

// 获取子节点总数。
long XmlNode::GetChildNodeCount() const
{
return (long)this->m_Childs.size();
}

// 获取父节点。
const XmlNode* XmlNode::GetPater() const
{
return this->m_Pater;
}

// 获取子节点。
const XmlNode* XmlNode::GetChildNode( long id ) const
{
if( id < 0 || id >= GetChildNodeCount() )
return NULL;

return &( m_Childs[id] );
}

// 获取本节点的次序。
long XmlNode::GetNodeId() const
{
// 如果没有父节点,则返回0。
if( !m_Pater )
return 0;

// 寻找节点。
for( unsigned int i = 0; i < m_Pater->m_Childs.size(); i ++ )
{
if( &m_Pater->m_Childs == this )
return i;
}

// 没找到节点。
assert( !"没有在父节点的子节点中找到自己。");

//
return -1;
};

// 获取前一个节点。
const XmlNode* XmlNode::GetPrevNode() const
{
if( !m_Pater )
return NULL;

long id = GetNodeId();

if( id < 1 || id >= GetChildNodeCount() )
return NULL;

return &this->m_Pater->m_Childs[ id - 1 ];
}

// 获取后一个节点。
const XmlNode* XmlNode::GetNextNode() const
{
if( !m_Pater )
return NULL;

long id = GetNodeId();

if( id < 0 || id >= GetChildNodeCount() - 1 )
return NULL;

return &this->m_Pater->m_Childs[ id + 1 ];
}

// 查询子节点。
const XmlNode* XmlNode::QueryChild( const char* name ) const
{
std::vector< XmlNode >::const_iterator it = m_Childs.begin();

while( it != m_Childs.end() )
{
if( it->GetName() )
{
if( !strcmp( it->GetName(), name ) )
return &*it;
}
++ it;
}

return NULL;
};

// 根据缓冲区指针构造节点。
XmlNode::XmlNode( XmlNode* pater )
: m_sNameBegin( NULL ),
m_sNameEnd( NULL ),
m_sDataBegin( NULL ),
m_sDataEnd( NULL ),
m_Pater( pater )
{
};

XmlNode::~XmlNode()
{
};

/*
构造。
*/
XmlDocument::XmlDocument(void)
: m_sBuffer(NULL),
m_XmlNodeRoot(NULL)
{
};

/*
析构。
*/
XmlDocument::~XmlDocument(void)
{
if( m_sBuffer && m_XmlNodeRoot )
{
Close();
}
};

/*
说明 打开XML文挡并解析。
返回 打开是否成功。
参数 为XML文件名字。
*/
bool XmlDocument::Open( const char* sFileName )
{
// 打开文件。
FILE* fp = fopen( sFileName, "rt" );

if( !fp )
return false;

// 读入。
fseek( fp, 0, 2 );
long len = ftell( fp );
fseek( fp, 0, 0 );

// 如果小于2M
if( len < 1 << 21 )
{
m_sBuffer = (char*)malloc( len + 1 );

if( !m_sBuffer )
return false;

fread( m_sBuffer, len, 1, fp );
}

// 关闭文件。
fclose( fp );

// 解析。
m_XmlNodeRoot = new XmlNode( NULL );
char* sBuffer = m_sBuffer;

if( !m_XmlNodeRoot
|| !Parse( sBuffer, *m_XmlNodeRoot )
)
{
Close();
return false;
}

//
return true;
};

/*
说明 关闭XML文挡,释放内存。
*/
void XmlDocument::Close( )
{
//
assert( m_sBuffer );
assert( m_XmlNodeRoot );

// 释放
free( m_sBuffer );
delete m_XmlNodeRoot;
};

/*
说明 获取引导节点。
返回 引导节点。
*/
const XmlNode* XmlDocument::GetRoot()
{
//
assert( m_sBuffer );
assert( m_XmlNodeRoot );

//
return m_XmlNodeRoot;
};

71

主题

1330

帖子

2585

积分

金牌会员

Rank: 6Rank: 6

积分
2585
发表于 2006-6-13 09:16:00 | 显示全部楼层

Re:我们写的一个小巧的XML解析类,感兴趣的可以用用。

能不能当附件上传。
谢谢。

18

主题

971

帖子

982

积分

高级会员

Rank: 4

积分
982
发表于 2006-6-13 10:45:00 | 显示全部楼层

Re:我们写的一个小巧的XML解析类,感兴趣的可以用用。

无言中.....

23

主题

515

帖子

552

积分

高级会员

Rank: 4

积分
552
发表于 2006-6-13 12:06:00 | 显示全部楼层

Re:我们写的一个小巧的XML解析类,感兴趣的可以用用。

勇气可佳,但多此一举。

祥看TinyXML:)

30

主题

569

帖子

569

积分

高级会员

Rank: 4

积分
569
 楼主| 发表于 2006-6-13 15:44:00 | 显示全部楼层

Re:我们写的一个小巧的XML解析类,感兴趣的可以用用。

  写这个东西主要是因为要把XML解析纳入我们的性能监控和调试系统中,放在这里希望为开源做一点点抛砖引玉的贡献。TinyXML当初研究过,现在想想当时没有采用确实是有点遗憾的。附件不知道怎么上传,直接拷到文件中就能用了,第一个是H文件的内容,第二个是CPP文件的内容,GAMEWARNING可去掉或换成警告函数。

140

主题

1228

帖子

1233

积分

金牌会员

Rank: 6Rank: 6

积分
1233
QQ
发表于 2006-6-13 16:10:00 | 显示全部楼层

Re:我们写的一个小巧的XML解析类,感兴趣的可以用用。

最大的问题是如何控制字符集的问题?代码也称不上小巧。

30

主题

569

帖子

569

积分

高级会员

Rank: 4

积分
569
 楼主| 发表于 2006-6-13 16:39:00 | 显示全部楼层

Re:我们写的一个小巧的XML解析类,感兴趣的可以用用。

  欢迎大家多提意见,让偶在阳光下成长。
  这个只是简单的实现了读入,解析,没有实现插入等功能。主要是目前还用不上。我们使用中文的XML语句。
  :)代码行数还是满少的,500行左右。

21

主题

120

帖子

129

积分

注册会员

Rank: 2

积分
129
发表于 2006-6-13 17:29:00 | 显示全部楼层

Re:我们写的一个小巧的XML解析类,感兴趣的可以用用。

决定把你的代码纳入我的通用库中
毕竟我是不太愿意为了小小目的就把tinyxml引入的人

30

主题

569

帖子

569

积分

高级会员

Rank: 4

积分
569
 楼主| 发表于 2006-6-14 12:08:00 | 显示全部楼层

Re:我们写的一个小巧的XML解析类,感兴趣的可以用用。

不胜荣幸
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2026-1-24 20:37

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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