解析Html生成标签树(一)

解析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的结束位置。
5. 对于<Tag>xxx</Tag>之间的字符串xxx,将其作为特殊的HtmlTextTag处理。出栈,和入栈操作与普通Tag类似。
6. 当栈为空的时候表示最后一次出栈的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

        {

            //对于<AAA>xxx</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>文本段1<B>文本段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就可以遍历出所有的节点了。上面仅仅是给出了大概的方法,不过我相信要将上面的方法转换成可运行代码,各位都是有这个能力的。。。

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭