新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> DTD, XML Schema(XMLS), RELAX NG
    [返回] 中文XML论坛 - 专业的XML技术讨论区XML.ORG.CN讨论区 - XML技术『 DTD/XML Schema 』 → XML 问题 #13 :XML 和压缩 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 8871 个阅读者浏览上一篇主题  刷新本主题   平板显示贴子 浏览下一篇主题
     * 贴子主题: XML 问题 #13 :XML 和压缩 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     anchen0617 帅哥哟,离线,有人找我吗?双子座1983-6-17
      
      
      威望:5
      等级:大二(研究C++)
      文章:281
      积分:3413
      门派:XML.ORG.CN
      注册:2004/10/17

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给anchen0617发送一个短消息 把anchen0617加入好友 查看anchen0617的个人资料 搜索anchen0617在『 DTD/XML Schema 』的所有贴子 访问anchen0617的主页 引用回复这个贴子 回复这个贴子 查看anchen0617的博客楼主
    发贴心情 XML 问题 #13 :XML 和压缩

    本 XML 问题专栏探索了几种压缩 XML 文档的方法。XML 中的特殊结构允许它对最原始的压缩技术进行某些改进。专栏作者 David Mertz 阐明了利用它们的几种方法,以及包含演示这些技术的样本代码。
    XML 作为一种格式具有许多优良的特性;它是表示任意数据结构的完美通用方法,并且它是人可读的(或多或少的)。但是 XML 有一个非常显著的缺点。XML 文档是冗长的;并不仅是在文字方面有点冗长,其大小也几乎都是令人难以置信的巨大。多数时候,XML 的这个缺点实际上没什么影响。DASD 是便宜的,并且线路上的时间可能仅是过程中总时间的一小部分。但其他时候,带宽和存储空间可能是重要的。

    要量化事物,对于表示面向表格数据的 XML 文档是 CSV 或数据库表示或甚至是平面文件的三倍大是再平常不过的。类似的增长在表示 EDI 数据中是很典型的(例如对于 ebXML 项目)。在许多这样的环境中,人们是从有好几兆字节的数据源着手。使这些文件增大几倍会带来不便,特别是用于传输目的。例如,出于本文目的我创建了一个大约一兆字节的 Apache 访问日志文件的 XML 表示。这创建了是基本面向行日志的 3.18 倍大小的 XML 文档。此过程中添加的唯一信息是一些字段的描述名称,但那本来也可以使用不超过一百个字节的、简单的标题行指定。此外,我的特定 XML 表示没有在标记中包含任何名称空间,它会进一步增加大小。

    当考虑压缩文档时,通常首先考虑常用的压缩算法,如:Lempel-Ziv 和 Huffman,以及在它们上面实现变化的一些常用实用程序。特别是,在类 Unix 平台上,首先想到的通常是实用程序 gzip;在其它平台上,zip 更为常用(使用实用程序如:PKZIP、Info-ZIP 和 WinZip)。现已证明 gzip 始终要优于 zip,但使用的人较少。这些实用程序实际上意在充分地减少 XML 文件的大小。但是,同样证明了通过两种方法 — 单独或组合可以获得相当好的压缩率。

    第一种技术使用 Burrows-Wheeler 压缩算法而不是顺序 Lempel-Ziv 算法。特别是,相对较少使用的实用程序 bzip2 (或者与其相关库和 API)是许多系统平台上的 Burrows-Wheller 的实现(并且伴随完整的源文件和 BSD 风格的许可证)。Burrows-Wheeler 通过对未压缩的源文件中的相关字符串进行分组来操作,而不是象 Lempel-Ziv 风格那样构建一个字符串重现的字典。在许多文件类型上,bzip2 始终比 gzip 获得较好的压缩效果,而对 XML 文档的效果尤其显著。bzip2 通常较 gzip 慢是其不足之处。再次,低带宽常会掩盖 CPU 或内存限制的算法中的速度差异。

    第二种技术是利用 XML 文档非常特定的结构生成更可压缩的表示。XMill 实用程序就是这种技术的一种实现,遵守 AT&T 的自由许可证就可以使用(带 C++ 源文件)。然而,看起来 XMill 许可证上需要某些点击完成风格的限制,因而不能由其他方直接发布(至少我是这样理解的)。我对同一通用技术创建了自己的实现,并且我会在本文中呈现该实现。此处代码向公众域发布,实现的技术由本人独立开发且与 XMill 有“鸟瞰”的类似性:有时 XMill 更好,有时我更好(但 XMill 可能总是比我的初始实现更快,因其只关注压缩结果)。

    比较基本算法
    本文中,我获取或创建了四个基本文档用于比较的目的。第一个是莎士比亚的戏剧 哈姆雷特作为 XML 文档(请参阅参考资料)。标记中包括如 <PERSONA>、<SPEAKER> 和 <LINE> 等标记,这些十分自然地映射到人们可能在印刷拷贝中遇到的排版形式。为了对 XML 标记如何有助于文档大小和可压缩性作比较,我从 hamlet.xml 派生了一个文档 hamlet.txt,仅仅只是删去所有 XML 标记而保留其内容。这种派生是不可逆的且是一种信息的绝对丢失。例如,阅读 hamlet.txt 的人从语义上去确定哪个内容是“speaker”名称以及哪一个是“行”不会有什么大困难,而计算机可能无法轻易地重新生成源 XML 文档。

    下两个文件是 Apache Weblog 文件(一组简洁的面向行的记录)及从这个文件创建的 XML 文档。因为源文档是日志文件,在转换中无信息丢失,而从 XML 重新创建原始格式的文档非常繁琐。当然,对于日志文件格式您不能使用 XML 解析器,或DOM,或验证器,或 DTD。让我们看看清单 1 中的基本文档大小。

    清单 1 .基本文档的目录清单
    288754  hamlet.xml
    188830  hamlet.txt
    949474  weblog.txt
    3021921  weblog.xml


    在两种案例中,XML 格式文档都非常大。在哈姆雷特示例中,由于文本版本中实际信息内容也减少而使比较不是完全公平。但是对于 Weblog 文件,XML 开始看上去相当槽糕。但凡事皆不可只看其表面。压缩程序做了一件相当好的工作 — 将文档压缩到其实际信息内容大小,并且在已压缩版本中无意义的填充趋向于零大小(如果一切顺利的话,压缩是渐进地)。让我们在清单 2 和清单 3 中尝试 gzip、zip 和 bzip2 。

    清单 2 .已压缩的莎士比亚目录清单
      78581  hamlet.xml.gz
      72505  hamlet.txt.gz
      78696  hamlet.xml.zip
      72620  hamlet.txt.zip
      57522  hamlet.xml.bz2
      56743  hamlet.txt.bz2


    清单 3 .已压缩的 Weblog 目录清单
      91029  weblog.txt.gz
    115524  weblog.xml.gz
      91144  weblog.txt.zip
    115639  weblog.xml.zip
      56156  weblog.txt.bz2
      66994  weblog.xml.bz2


    通过研究两个清单中的文件大小显现出来一些有趣的事情。对于两种风格的文档而言(对于每种压缩技术),已压缩文件中的文件大小差异比 XML 和文本原始文件之间的差异要小。同样值得注意的是:对应的案例中 gzip 和 zip 非常接近形成一组,而 bzip2 始终都做得更好。此外,当对莎士比亚文档使用 bzip2 时,文本格式和 XML 格式之间压缩大小差异几乎可忽略,尽管 XML 基本文档比文本格式的大 53% 。

    而 Weblog 代表了一个问题案例。虽然压缩相当多地减小了 XML 转换的膨胀,即使 bzip2 版本仍然让 XML 标记使大小增加了大约 20% 。这未必是灾难,感觉好象我们应当能够将其压缩至真实的信息内容大小。

    可逆转换
    当 XML 文档涉及压缩时有效率相当低的形式,正如您将看到的,bzip2 通过对字符串重新分组就稍微减轻了这种低效性。就本质而言,XML 文档是十分不同部分的混合物 — 不同类型的标记、属性和元素体。如果您能获取每个相对一致的事物的集合并且在已转换的文件中将它们相互紧密分组,标准压缩程序将会有很多工作要处理。例如:如果 Weblog 中每个 <host> 标记体出现在另一个附近,包含主机 IP 地址的那块东西就非常易于压缩。本处的技巧在于:找到将 XML 文档转换成包含所有相同信息的一种方法,而以一种对压缩程序友好的风格构造布局。

    实用程序 xml2struct.py 和 struct2xml.py 恰恰能做我们所希望的。下面的版本包含了“几点限制”,但它们演示了涉及的原理。一些限制在于每个文档的不同标记限制为 253 个,并且不处理属性和处理指令。而修订那些限制不会改变结果的本质。XMill 执行类似的转换,但带有一些额外选项和较少的限制。

    "struct"文档的常用格式如下:

    原始 XML 文档中出现的标记列表,由新行字符分隔。
    章节分隔符:0x00 (空字节)
    总体文档结构的紧凑表示,每个开始标记由单一字节表示,内容的出现由 0x02 字节标记。
    另一个章节分隔符:0x00 (空字节)
    在文档结构示意图中显示的所有元素的内容,按元素类型分组。每个单独的内容项由 0x02 字节分隔,而新类型元素的开始由 0x01 字节分隔(最后的一个并非严格必需的,但它使逆向转换更简便)。

    下面是实现和逆转所描述转换的完整 Python 代码。用另一个编程语言实现这一转换同样也是简单的:

    清单 4 .xml2struct.py
    import sys
    import xml.sax
    from xml.sax.handler import *

    class StructExtractor(ContentHandler):
        """Create a special structure/content form of an XML document"""
        def startDocument(self):
            self.taglist = []
            self.contentdct = {}
            self.state = []             # stack for tag state
            self.newstate = 0           # flag for continuing chars in same elem
            self.struct = []            # compact document structure

        def endDocument(self):
            sys.stdout.write('\n'.join(self.taglist))
                                        # Write out the taglist first
            sys.stdout.write(chr(0))    # section delimiter \0x00
            sys.stdout.write(''.join(self.struct))
                                        # Write out the structure list
            sys.stdout.write(chr(0))    # section delimiter \0x00
            for tag in self.taglist:    # Write all content lists
                sys.stdout.write(chr(2).join(self.contentdct[tag]))
                sys.stdout.write(chr(1)) # delimiter between content types

        def startElement(self, name, attrs):
            if not name in self.taglist:
                self.taglist.append(name)
                self.contentdct[name] = []
                if len(self.taglist) > 253:
                    raise ValueError, "More than 253 tags encountered"
            self.state.append(name)     # push current tag
            self.newstate = 1           # chars go to new item
                                        # single char to indicate tag
            self.struct.append(chr(self.taglist.index(name)+3))

        def endElement(self, name):
            self.state.pop()            # pop current tag off stack
            self.newstate = 1           # chars go to new item
            self.struct.append(chr(1))  # \0x01 is endtag in struct

        def characters(self, ch):
            currstate = self.state[-1]
            if self.newstate:           # either add new chars to state item
                self.contentdct[currstate].append(ch)
                self.newstate = 0
                self.struct.append(chr(2))
                                        # \0x02 content placeholder in struct
            else:                       # or append the chars to current item
                self.contentdct[currstate][-1] += ch

    if __name__ == '__main__':
        parser = xml.sax.make_parser()
        handler = StructExtractor()
        parser.setContentHandler(handler)
        parser.parse(sys.stdin)

    使用 SAX 而不是 DOM 使这一转换相当节省时间,即使时间不是开发它的主要考虑事项。要逆向转换,可以使用清单 5 。

    清单 5.struct2xml.py
    def struct2xml(s):
        tags, struct, content = s.split(chr(0))
        taglist = tags.split('\n')      # all the tags
        contentlist = []                # list-of-lists of content items
        for block in content.split(chr(1)):
            contents = block.split(chr(2))
            contents.reverse()          # pop off content items from end
            contentlist.append(contents)
        state =  []                     # stack for tag state
        skeleton = []                   # templatized version of XML
        for c in struct:
            i = ord(c)
            if i >= 3:                  # start of element
                i -= 3                  # adjust for struct tag index offset
                tag = taglist[i]        # spell out the tag from taglist
                state.append(tag)       # push current tag
                skeleton.append('<%s>' % tag)
                                        # insert the element start tag
            elif i == 1:                # end of element
                tag = state.pop()       # pop current tag off stack
                skeleton.append('</%s>' % tag)
                                        # insert the element end tag
            elif i == 2:                # insert element content
                tag = state[-1]
                item = contentlist[taglist.index(tag)].pop()
                item = item.replace('&','&')
                skeleton.append(item)   # add bare tag to indicate content
            else:
                raise ValueError, "Unexpected structure tag: ord(%d)" % i

        return ''.join(skeleton)

    if __name__ == '__main__':
        import sys
        print struct2xml(sys.stdin.read()),

    压缩转换结果
    当尝试压缩结果时,所讨论转换的真正要点来了。如果一切都按计划进行,foo.struct 比 foo.xml 更可压缩,即便是两者包含同样的信息(这是可证明的,因为二者是对称可派生的)。从某种意义上,xml2struct.py 已经是一种压缩算法(它生成了相对较小的文件),但真正要点是并非直接使用它而是将其作为进一步压缩的基础。

    让我们看看一些大小,包括几个上面重复提及的。XMill 产生的某些结果也放入以作比较;您将能够因包含名称 .xmi 而识别出它们。(XML 有使用 gzip 和 bzip2 算法的版本。)

    清单 6.“结构化 XML”的目录清单
    228610  hamlet.struct
      57533  hamlet.struct.bz2
      57522  hamlet.xml.bz2
      71060  hamlet.struct.gz
      78581  hamlet.xml.gz
      61823  hamlet.xmi.bz2
      75247  hamlet.xmi.gz


    这段文档上的结果稍嫌混杂。“重新构造” XML 文档对 gzip 相当有帮助。原始 XML 文件上普通的旧 bzip2 在生成可压缩的结构上比我的尝试好 11 个字节。当然,值得欣慰的是 XMill 有类似的表现,但比我的转换差。

    该技术对 Weblog 文件做的相当好。这里它实际产生作用。

    清单 7.“结构化 XML”的目录清单 2
    1764816  weblog.struct
      59955  weblog.struct.bz2
      66994  weblog.xml.bz2
      56156  weblog.txt.bz2
      76183  weblog.struct.gz
    115524  weblog.xml.gz
      59409  weblog.xmi.bz2
      74439  weblog.xmi.gz


    如前所述,重新构建 XML Weblog 极大地帮助了 gzip 压缩。但由于 gzip 不再是我喜爱的技术,因而仅能吸引我部分注意力。我真正的兴趣在于我已经将这个已经相当好的 bzip2 压缩率改进了 11%。虽然可能没什么大不了,但对于为兆字节而发愁的问题来说已经足够。不管怎样,本例中 XMill 改进比 xml2struct.py 更好。同样令人感兴趣的是,这一重新构建的 XML 的压缩在原始文本形式的 Weblog 获得的最佳压缩的 7% 以内。

    结束语
    尽管这里呈现的实用程序是一种初步的尝试,即便是在这一早期形式中它也做得令人称奇的好 — 至少在某些情况下 -- 从压缩的 XML 文件中榨干最后那几个字节。经过一些改进和实验,我期望能获得几个百分点的降低。使编写这一实用程序困难的一部分原因在于 bzip2 从一开始就完成了如此完美的工作。当我开始凭经验测试时,我实在惊奇 Burrows-Wheeler 算法的有效性。

    某些商业实用程序尝试利用压缩文档的特定 DTD 知识的方式执行 XML 压缩。这些技术相当有希望获得附加压缩。xml2struct.py 和 XMill 作为简单的命令行工具的优点在于:您可以透明地应用于 XML 文件。但是,每次压缩定制编程并非总是值得或可能的。榨干更多字节也许是可达到的目标。


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    xml这门语言太好了,我们共同努力吧!!!!!

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2004/11/13 15:07:00
     
     GoogleAdSense双子座1983-6-17
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 DTD/XML Schema 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/4/18 4:09:26

    本主题贴数1,分页: [1]

     *树形目录 (最近20个回帖) 顶端 
    主题:  XML 问题 #13 :XML 和压缩(12546字) - anchen0617,2004年11月13日

    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    109.375ms