Today's the day

向软件大牛炫耀我会焊单片机,向硬件大牛炫耀我会写 Rails,向软硬件大牛炫耀我生物,向软硬件生物大牛炫耀我会折腾期货 -_-bbb

自动闭合 html 标签简化版

Chito 现在用 Rubyful Soup 来解决 html 没有正确闭合的问题,其中的 BeautifulSoup 类的 prettify 方法非常强大,几乎可以修正 html 中的所有错误。

不过 Chito 只是拿来闭合标签,其他的功能完全用不到,Rubyful Soup 还要依赖 htmltools 这个库,为了个闭合标签就要多附带这么多库太不爽了,而且还有个 %nbsp 的 bug。

自己尝试写了个,close_html:

  1. def close_html(html)
  2.     stream = html.scan( /<\s*[^>]+>/ ).map {|x| x[ /<\s*([^>^\s]*)[^>]*>/ , 1]}
  3.     stack = []
  4.     stream.each do |x|
  5.         if x =~ /^\//
  6.             stack.pop if x[1..-1] == stack.last
  7.         else
  8.             stack.push x
  9.         end
  10.     end
  11.     stack.reverse.each {|x| html << "</#{x}>\n"}
  12.     html
  13. end

处理的方式罗嗦了一些,先把 html 文本中的标签提取出来,然后再去掉属性之类的,转换成一个 “标签流”,比如:

<div class="main><ul><li>test</li>

就转换成了:

["div","ul","li""/li"]

接着读取上面这个流,把开始标签压栈,遇到以 / 开头的结束标签,检查是否和栈顶的标签相匹配,匹配就弹出,走完标签流后,还留在栈里的标签就是需要闭合的标签了。

测试一下:

  1. test_html = <<END
  2. <div>
  3.     <ul>
  4.         <li> lalalalala </li>
  5.         <li><strong><a href="http://www.google.com"> Google
  6. END
  7.  
  8. puts close_html(test_html)

输出:

  1. <div>
  2.     <ul>
  3.         <li> lalalalala </li>
  4.         <li><strong><a href="http://www.google.com"> Google
  5. </a>
  6. </strong>
  7. </li>
  8. </ul>
  9. </div> 

效果不错,标签都正确闭合了~

这种方法还是有局限性的,因为只是个简单的栈操作,所以只适合修正那些正确的 html 被拦腰截断的情况,而不能修正类似 <h1></h2> 这样匹配错误的情况,<div><a></div> 这样中间格式错误的情况也不行,不过 Chito 的编辑器和 textile 都不会生成这些样子的 html,所以完全可以用这个小函数替代那个庞大的 Rubyful Soup 了~