解析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给根节点。   由于是在公司内部开发的东西,所以不可能把源代码拿出来粘贴,所以只能把大概的代码给出。
伪代码如下:
  1. public void Parse()   
  2.   
  3. {   
  4.   
  5.     char ch = GetCurrentChar();  //取第一个字符      
  6.   
  7.     while (!Eof())   
  8.   
  9.     {   
  10.   
  11.         if (ch == '<')   
  12.   
  13.         {   
  14.   
  15.             ch = MoveNext();     //取下一个字符      
  16.   
  17.             if ((ch >= 'A') && (ch <= 'Z') || (ch == '!'))   
  18.   
  19.             {   
  20.   
  21.                 iBeginPos = Index;       //记录开始位置   
  22.   
  23.                 //表示可能是一个标签    
  24.   
  25.                 HtmlTag tag = GetTag();  //解析此Tag      
  26.   
  27.                 if (tag != null)   
  28.   
  29.                 {   
  30.   
  31.                     //首先判断是否有文本      
  32.   
  33.                     if (m_CurrentText.Lenght > 0)   
  34.   
  35.                     {   
  36.   
  37.                         //将文本作为一个普通Tag入栈      
  38.   
  39.                         Stack.Push(new HtmlTextTag(m_CurrentText));   
  40.   
  41.                     }   
  42.   
  43.                     tag.BeginPos = iBeginPos;   //记录此Tag的开始位置      
  44.   
  45.                     Stack.Push(tag);            //把Tag入栈      
  46.   
  47.                 }   
  48.   
  49.             }   
  50.   
  51.   
  52.   
  53.             ch = GetCurrentChar();   
  54.   
  55.             if (ch == '/')   
  56.   
  57.             {   
  58.   
  59.                 //可能是结束标签      
  60.   
  61.                 tagName = GetTagName();   
  62.   
  63.                 //从上到下查看Stack,如果Tag中存在      
  64.   
  65.                 if (FindInStack(tagName))   
  66.   
  67.                 {   
  68.   
  69.                     //在栈中找到名为tagName的元素,则把找到的元素出栈      
  70.   
  71.                     PopTag(tagName);   
  72.   
  73.                 }   
  74.   
  75.             }   
  76.   
  77.         }   
  78.   
  79.         else  
  80.   
  81.         {   
  82.   
  83.             //对于<AAA>xxx</AAA>之间的文本xxx,这里将作为TextTag来处理     
  84.   
  85.             m_CurrentText.Append(GetCurrentChar());   
  86.   
  87.         }   
  88.   
  89.         //继续处理下一个字符      
  90.   
  91.         ch = MoveNext();   
  92.   
  93.     }   
  94.   
  95.   
  96.   
  97.     //解析完成以后,如果栈不空,那么把元素出栈,并把最后一次出栈的元素作为根      
  98.   
  99.     if (Stack.Count > 0)   
  100.   
  101.     {   
  102.   
  103.         HtmlTag tag = null;   
  104.   
  105.         while (Stack.Count > 0)   
  106.   
  107.         {   
  108.   
  109.             tag = Stack.Pop();   
  110.   
  111.             PopTag(tag);   
  112.   
  113.         }   
  114.   
  115.   
  116.   
  117.         //最后一个元素作为根元素      
  118.   
  119.         if (tag != null)   
  120.   
  121.         {   
  122.   
  123.             m_listRoot.Add(tag);   
  124.   
  125.         }   
  126.   
  127.     }   
  128.   
  129. }   
  130.   
  131.   
  132.   
  133. private void PopTag(HtmlTag tag)   
  134.   
  135. {   
  136.   
  137.     int iLevel = Stack.Count;   
  138.   
  139.   
  140.   
  141.     //找到了元素,把iLevel到m_IMaxLevel中所有的元素按照全部作为tag的子元素      
  142.   
  143.     for (int i = iLevel + 1; i < m_iMaxLevel; i++)   
  144.   
  145.     {   
  146.   
  147.         for (j = 0; j < HashTable[i].Count; j++)   
  148.   
  149.         {   
  150.   
  151.             tag.Children.Add(HashTable[i][j]);   
  152.   
  153.         }   
  154.   
  155.     }   
  156.   
  157.   
  158.   
  159.     //表示栈已经为空,那么最后一次出栈的tag将作为根      
  160.   
  161.     if (Stack.Count == 0)   
  162.   
  163.     {   
  164.   
  165.         m_listRoot.Add(tag);   
  166.   
  167.     }   
  168.   
  169. }   
  170.   
  171.   
  172.   
  173. private void PopTag(string tagName)   
  174.   
  175. {   
  176.   
  177.     /*  
  178.  
  179.      * 元素出栈的时候,首先需要把当前已经存在了的HtmlTextTag入栈  
  180.  
  181.      * 比如:<A>文本段1<B>文本段2</B>文本段3</A>  
  182.  
  183.      * 在Parse中,当解析出<B>入栈前,需要先把"文本段1"入栈  
  184.  
  185.      * 在这里,解析出了</B>结束标志  
  186.  
  187.      * 那么首先需要把"文本段2"入栈。  
  188.  
  189.      * 解析出</A>则需要把"文本段3'入栈。  
  190.  
  191.      * 这样才能够保证"文本段1"和"文本段3"成为<A>的子节点,而"文本段2"作为<B>的子节点  
  192.  
  193.      */  
  194.   
  195.     if (m_CurrentText.Lenght > 0)   
  196.   
  197.     {   
  198.   
  199.         //将文本作为一个普通Tag入栈      
  200.   
  201.         Stack.Push(new HtmlTextTag(m_CurrentText));   
  202.   
  203.     }   
  204.   
  205.   
  206.   
  207.     HtmlTag tag = Stack.Pop();  //元素出栈      
  208.   
  209.     int iLevel = Stack.Count;   //记录栈元素数      
  210.   
  211.   
  212.   
  213.     while (tag.Name != tagName)   
  214.   
  215.     {   
  216.   
  217.         //将tag放入第iLevel层的List中      
  218.   
  219.         HashTable[iLevel].Add(tag);   
  220.   
  221.         tag = Stack.Pop();   
  222.   
  223.         iLevel = Stack.Count;   
  224.   
  225.     }   
  226.   
  227.   
  228.   
  229.     //元素出栈后续处理      
  230.   
  231.     PopTag(tag);   
  232.   
  233. }   
  234.   
  235.   
  236.   
  237. private HtmlTag GetTag()   
  238.   
  239. {   
  240.   
  241.     if ("如果发现是 < !--开头的元素"//则表示是注释      
  242.   
  243.     {   
  244.   
  245.         SkipComment();   
  246.   
  247.     }   
  248.   
  249.   
  250.   
  251.     HtmlTag tag = new HtmlTag();   
  252.   
  253.     tag.Name = GetTagName();   
  254.   
  255.     //这里的Attribute我将其作为HashTable类型,Hash[属性名]=属性值      
  256.   
  257.     tag.Attribute = GetTagAttribute();   
  258.   
  259.     return tag;   
  260.   
  261. }   

解析结束以后,通过访问m_listRoot就可以遍历出所有的节点了。上面仅仅是给出了大概的方法,不过我相信要将上面的方法转换成可运行代码,各位都是有这个能力的。。。

 

posted @ 2008-09-07 21:29 简单就好 阅读(...) 评论(...) 编辑 收藏