解析Html成标签树结构以后,我们不但可以很容易取得想要的元素,同时也很容易将Html转换成对应的XML文件。但是由于代码是在公司写的,所以没有粘贴出来的可能性,所以我只能给出大概的代码流程,具体细节描述,相信各位都很容易写出来,并且写的比我好,关键的是算法实现思想。算法的关键如下:
1. Html中每个tag都是都将作为树中的一个节点存在的,每个tag都属于树中的某一层。
2. 辅助数据结构:栈(stack)、List、HashTable。其中HashTable[i](i属于int类型)是一个List,用于临时存储第i层子Tag。
3. 顺序扫描Html文本,当遇到”A~Z”这样的标志,表示可能是一个Tag,调用GetTag()函数对此段代码进行解析,解析出Tag名,Tag属性等等。如果返回值不为空,那么将返回值入栈。并且记录次tag的开始位置。
4. 遇到/A~Z这样的标志,表示可能是某个Tag的结束。解析出此结束标志的Tag名。如果在栈中找到与此结束标志名同名的元素(此元素属于栈中第iLevel层),那么表示找到匹配的Tag。则Tag出栈,将HashTable[iLevel+1]到HashTable[maxLevel]中的所有元素取出作为此Tag的子节点。放入第HashTable [iLevel]中。并记录Tag的结束位置。
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/webkaifa/)5. 对于Tagxxx/Tag之间的字符串xxx,将其作为特殊的HtmlTextTag处理。出栈,和入栈操作与普通Tag类似。
6. 当栈为空的时候表示最后一次出栈的Tag给根节点。 由于是在公司内部开发的东西,所以不可能把源代码拿出来粘贴,所以只能把大概的代码给出。
伪代码如下:
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/webkaifa/)view plaincopy to clipboardprint?
public void Parse()
{
char ch = GetCurrentChar(); //取第一个字符
while (!Eof())
{
if (ch == '')
{
ch = MoveNext(); //取下一个字符
if ((ch = 'A') && (ch = 'Z') || (ch == '!'))
{
iBeginPos = Index; //记录开始位置
//表示可能是一个标签
HtmlTag tag = GetTag(); //解析此Tag
if (tag != null)
{
//首先判断是否有文本
if (m_CurrentText.Lenght 0)
{
//将文本作为一个普通Tag入栈
Stack.Push(new HtmlTextTag(m_CurrentText));
}
tag.BeginPos = iBeginPos; //记录此Tag的开始位置
Stack.Push(tag); //把Tag入栈
}
}
ch = GetCurrentChar();
if (ch == '/')
{
//可能是结束标签
tagName = GetTagName();
//从上到下查看Stack,如果Tag中存在
if (FindInStack(tagName))
{
//在栈中找到名为tagName的元素,则把找到的元素出栈
PopTag(tagName);
}
}
}
else
{
//对于AAAxxx/AAA之间的文本xxx,这里将作为TextTag来处理
m_CurrentText.Append(GetCurrentChar());
}
//继续处理下一个字符
ch = MoveNext();
}
//解析完成以后,如果栈不空,那么把元素出栈,并把最后一次出栈的元素作为根
if (Stack.Count 0)
{
HtmlTag tag = null;
while (Stack.Count 0)
{
tag = Stack.Pop();
PopTag(tag);
//最后一个元素作为根元素
if (tag != null)
{
m_listRoot.Add(tag);
}
}
}
private void PopTag(HtmlTag tag)
{
int iLevel = Stack.Count;
//找到了元素,把iLevel到m_IMaxLevel中所有的元素按照全部作为tag的子元素
for (int i = iLevel + 1; i m_iMaxLevel; i++)
{
for (j = 0; j HashTable[i].Count; j++)
{
tag.Children.Add(HashTable[i][j]);
}
}
//表示栈已经为空,那么最后一次出栈的tag将作为根
if (Stack.Count == 0)
{
m_listRoot.Add(tag);
}
}
private void PopTag(string tagName)
{
/*
* 元素出栈的时候,首先需要把当前已经存在了的HtmlTextTag入栈
* 比如:A文本段1B文本段2/B文本段3/A
* 在Parse中,当解析出B入栈前,需要先把"文本段1"入栈
* 在这里,解析出了/B结束标志
* 那么首先需要把"文本段2"入栈。
* 解析出/A则需要把"文本段3'入栈。
* 这样才能够保证"文本段1"和"文本段3"成为A的子节点,而"文本段2"作为B的子节点
*/
if (m_CurrentText.Lenght 0)
{
//将文本作为一个普通Tag入栈
Stack.Push(new HtmlTextTag(m_CurrentText));
}
HtmlTag tag = Stack.Pop(); //元素出栈
int iLevel = Stack.Count; //记录栈元素数
while (tag.Name != tagName)
{
//将tag放入第iLevel层的List中
HashTable[iLevel].Add(tag);
tag = Stack.Pop();
iLevel = Stack.Count;
}
//元素出栈后续处理
PopTag(tag);
}
private HtmlTag GetTag()
{
if ("如果发现是 !--开头的元素") //则表示是注释
{
SkipComment();
}
HtmlTag tag = new HtmlTag();
tag.Name = GetTagName();
//这里的Attribute我将其作为HashTable类型,Hash[属性名]=属性值
tag.Attribute = GetTagAttribute();
return tag;
}
public void Parse()
{
char ch = GetCurrentChar(); //取第一个字符
while (!Eof())
{
if (ch == '')
{
ch = MoveNext(); //取下一个字符
if ((ch = 'A') && (ch = 'Z') || (ch == '!'))
{
iBeginPos = Index; //记录开始位置
//表示可能是一个标签
HtmlTag tag = GetTag(); //解析此Tag
if (tag != null)
{
//首先判断是否有文本
if (m_CurrentText.Lenght 0)
{
//将文本作为一个普通Tag入栈
Stack.Push(new HtmlTextTag(m_CurrentText));
}
tag.BeginPos = iBeginPos; //记录此Tag的开始位置
Stack.Push(tag); //把Tag入栈
}
}
ch = GetCurrentChar();
if (ch == '/')
{
//可能是结束标签
tagName = GetTagName();
//从上到下查看Stack,如果Tag中存在
if (FindInStack(tagName))
{
//在栈中找到名为tagName的元素,则把找到的元素出栈
PopTag(tagName);
}
}
}
else
{
//对于AAAxxx/AAA之间的文本xxx,这里将作为TextTag来处理
m_CurrentText.Append(GetCurrentChar());
}
//继续处理下一个字符
ch = MoveNext();
}
//解析完成以后,如果栈不空,那么把元素出栈,并把最后一次出栈的元素作为根
if (Stack.Count 0)
{
HtmlTag tag = null;
while (Stack.Count 0)
{
tag = Stack.Pop();
PopTag(tag);
}
//最后一个元素作为根元素
if (tag != null)
{
m_listRoot.Add(tag);
}
}
}
private void PopTag(HtmlTag tag)
{
int iLevel = Stack.Count;
//找到了元素,把iLevel到m_IMaxLevel中所有的元素按照全部作为tag的子元素
for (int i = iLevel + 1; i m_iMaxLevel; i++)
{
for (j = 0; j HashTable[i].Count; j++)
{
tag.Children.Add(HashTable[i][j]);
}
}
//表示栈已经为空,那么最后一次出栈的tag将作为根
if (Stack.Count == 0)
{
m_listRoot.Add(tag);
}
}
private void PopTag(string tagName)
{
/*
* 元素出栈的时候,首先需要把当前已经存在了的HtmlTextTag入栈
* 比如:A文本段1B文本段2/B文本段3/A
* 在Parse中,当解析出B入栈前,需要先把"文本段1"入栈
* 在这里,解析出了/B结束标志
* 那么首先需要把"文本段2"入栈。
* 解析出/A则需要把"文本段3'入栈。
* 这样才能够保证"文本段1"和"文本段3"成为A的子节点,而"文本段2"作为B的子节点
*/
if (m_CurrentText.Lenght 0)
{
//将文本作为一个普通Tag入栈
Stack.Push(new HtmlTextTag(m_CurrentText));
}
HtmlTag tag = Stack.Pop(); //元素出栈
int iLevel = Stack.Count; //记录栈元素数
while (tag.Name != tagName)
{
//将tag放入第iLevel层的List中
HashTable[iLevel].Add(tag);
tag = Stack.Pop();
iLevel = Stack.Count;
}
//元素出栈后续处理
PopTag(tag);
}
private HtmlTag GetTag()
{
if ("如果发现是 !--开头的元素") //则表示是注释
{
SkipComment();
}
HtmlTag tag = new HtmlTag();
tag.Name = GetTagName();
//这里的Attribute我将其作为HashTable类型,Hash[属性名]=属性值
tag.Attribute = GetTagAttribute();
return tag;
}
解析结束以后,通过访问m_listRoot就可以遍历出所有的节点了。上面仅仅是给出了大概的方法,不过我相信要将上面的方法转换成可运行代码,各位都是有这个能力的。。。