C#比较两个word文档的内容

 本文欢迎转载,但必须在文章显眼处保留原文地址

 http://www.cnblogs.com/Ss_Andy/archive/2010/11/02/1866762.html

       最近在开发的项目中,又遇到一个和word有关的问题,就是用户修改了一个word文档的内容后,需要将这个文档另一个事生成好的文档进行对比,看是否有修改.从而决定要不要提示用户修改另一文档..

      word文档的对比不像一般的txt文件,直接用File类进行读取操作.由于word的编码是没有公开的,所以想读取word里面的内容,还是得需要靠微软提供的office组件进行操作,不过这也麻烦,里面所提供几个读取的方法,好像都只是能读到纯文本而以,带格式都会给过滤掉,一般富格式都没有正常显示出来(比如flash,图片,表格),也许本人孤陋寡闻,在office组件中没有找到完美的解决方案,不过好在经过了几天的时间,也解决了这个问题,下面就说说解决方案吧!

 

       目前想到的就有三种方式可以解决,下面为你一一道来.

       一.第一种也是最简单方式,也是我第一时间想到的解决方案,可以将word文档转换成html文件,然后再将html文件转换成txt文件,然后用我们经常使用操作文件类File进行读取里面的文本(这时会包括html里面的标签,这样才能保留原先的格式),进行对比,这时读到的全是纯文档,在C#里面就直接当成字符串处理就得了!这个应该很容易很好理解吧!不过据比较牛X的同事说,因为word的功能非常强大,所以直接转换成html的话,可能会造成某些数据的丢失.PS:目前本人试过几次,都没有发现丢失的情况,可能测试的次数过少,或者说没有包函大量的数据吧!

           优点:易懂,简单.

           缺点:有可能造成丢失数据的风险.

      二.第二种方式有版本要求,需要是office2007或者2007以上的版本,2007以上的文档保存文件后缀是docx,你可以使用C#将他进行重命名,改成压缩文件的格式,然后再进行解压,你会看到很多资源文件(你可以先手动操作一下,看明白流程),然后再读取里面相关的xml文件,进行对比,但由于生成的文件过多,这个过程应该是相当烦琐的,所以,本人没有实践过,但知道肯定是可以实现的.同时要求电脑上要装有解压程序(这个一般都会有拉,不过还是要考虑的,)

           优点:应该是可以较完美的解决问题.也不会造成丢失数据的风险.

          缺点:操作烦琐,要弄明白所解压后的相关资源文件,加上对比的东西会比较多,这个过程相当相当烦琐.

      三.第三种,自认为是最完美的解决方法,目前项目也打算使用这种方式解决问题,下面说说具体的解决过程.

          优点:性能不错,也应该是可以完美解决问题,目前还没有发现别的问题.所有比较都让微软帮我们做了.

          缺点:还没发现..

          1.先添加引用Microsoft.Office.Interop.Word.

           2.接着让Microsoft.Office.Interop.Word.Application加载其中一个doc文档

              private Microsoft.Office.Interop.Word.Application _wordApplication;
              private Microsoft.Office.Interop.Word.Document _wordDocument;
              object nullobj = System.Reflection.Missing.Value;
              public void LoadWord(string fileName)
             {
                   this._wordApplication = new Microsoft.Office.Interop.Word.Application();
                  object objFile = fileName;
                  this._wordDocument = _wordApplication.Documents.Open(ref objFile, ref nullobj, ref nullobj,
                  ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj,
                   ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj);
               }

        3.再使用Microsoft.Office.Interop.Word.Application里面的一个比较功能

             object compareTarget = Microsoft.Office.Interop.Word.WdCompareTarget.wdCompareTargetSelected;
            object addToRecentFiles = false;
            _wordDocument.Compare(file, ref nullobj, ref compareTarget, ref nullobj, ref nullobj, ref addToRecentFiles, ref nullobj, ref nullobj);

          简单介绍一下这个Compare函数的功能,file就是你要对比的第二文件,

          compareTarget =Microsoft.Office.Interop.Word.WdCompareTarget.wdCompareTargetSelected;就是将对比后的结果加在第二个文档里面(其实就是修订拉,用过word修订功能的人都知道,他就是把你做的修改显示在word里面,当你鼠标移上去,他就会提示你,哪个用户在什么时候做了什么事情(比如:张三2010-11-1 19:00:00 删除ABC),可能这样说,好像很模糊一样,大家自己试一下就一目了解拉! 当然,我们要的不是这样,我们要的是直接告诉我们,哪里不一样!不要让他去修改第二个文档.但我找了很久,都没有相关的函数可以读取到这些修订信息.好在前阵子用过dsoframer.ocx这个web版本word控件,里面有提供了一个方法,可以读取到文档中的修订信息(具体的dsoframer.ocx使用方法,可以上网找,有好多相关资料的,也可以留言大家讨论讨论哈),下面贴出dsoframer获取修订信息的具体代码

 

         先让你界面上的dsoframer控件加载你的第二个文档,就是你调用Compare这个方法时传进去的file.然后下面再进行获取相关数据.

         var vCount = document.all.hideWord.GetRevCount();  //修改条数,比如删除一段话,vCount就是=1

         for(var i=1;i<=vCount;i++){

           //循环获取数据,i是表示第几条,2是表示做了什么操作  vOpt=1表示插入,=2表示删除

          //可以把下面的2改成1,就是获取是哪个用户做了修改,好像1是获取用户吧,哈哈,自己动手试下吧!

           var vOpt = document.all.hideWord.GetRevInfo(i,2);

           var context =  document.all.hideWord.GetRevInfo(i,3);  //3是表示获取修改了什么信息,比如用户删除了ABC,这里context就会等于"ABC"

         }

 

看了上面这段代码,应该知道,我们是可以获取文档哪里不一样的了!因为在调用Compare这个方法的时候,微软已经帮我们做了比较了,我们只是把相关的信息提取出来而以.

       不过上面的代码太片断了,我还是贴出我在项目中的代码吧,可能看上去比较清晰点..

 

//我项目中判断文档有没有改变,根据您自己的需求,可能这里会和我不一样!
function IsChange()
{
      var vCount = document.all.hideWord.GetRevCount();
      if(vCount%2!=0) return true;
      var vOpt;
      var Data="";
      for(var i=1;i<=vCount;i++)
      {
            //类型判断是否修改
           
            //1:插入
            //2:删除
            vOpt = document.all.hideWord.GetRevInfo(i,2);
            if(vOpt=="2") 
            {
                if(i==vCount) return;  //如果删除是最后一个,就表示有修改文档
                var nextvOpt = document.all.hideWord.GetRevInfo(i+1,2);
                if(nextvOpt=="2") return  true;//如果删除下面还是删除,就表示有修改文档
            }
            if(vOpt=="1")
            {
                if((i+1)<=vCount)
                {
                    var nextvOpt = document.all.hideWord.GetRevInfo(i+1,2);
                     if(nextvOpt=="1") return  true;//如果插入下面还是插入,就表示有修改文档
                }
            }
       }

       //下面这一段你可以不用管,是因为项目原因,需要忽略文档中的个别字符.
       for(var i=1;i<=vCount;i++){
            var context =  document.all.hideWord.GetRevInfo(i,3);
            var nextContext = document.all.hideWord.GetRevInfo(i+1,3);
            var replaceContext = context.replace("税目","品目")
            if(replaceContext!=nextContext) return true;
            i++;
       }
       return false;
}

 

简单解释一下,比如文件A里面是abc,文档B里面是abd,你用上面的方法读取第二文档的修订信息时,将是两条数据!第一条:删除了c. 第二条:插入了d

就是说你所有的修改,他都说先删除,再插入的,会有两条记录的!当然,如果没有删除旧的数据,只是添加,就是一条,删除也是一样!但是修改就是两条数据了.

 

根据上我面的代码,你可能会发现,修订信息是提取到了,但第二文档中也显示了修订信息,我们要的应该只是对比,而不要改到第二个文档.那这时可以使用office组件里面提供的一个方法进行接受修订信息(等会我会贴出操作office组件的类).

PS:如果第二word文档里面已经存在的修订信息的话,调用比较的方法会报错,所以我们在比较前,可以先让第二文档设置成接受修订(即使你没有修订信息也不会报错)

 

下面是我的 操作office组件的类(删除了大量与本贴无关的代码)

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace TariffItemLib.Common
{

    public class WordHelper
    {
        private Microsoft.Office.Interop.Word.Application _wordApplication;
        private Microsoft.Office.Interop.Word.Document _wordDocument;
        object nullobj = System.Reflection.Missing.Value;
        public WordHelper(string fileName)
        {
            this._wordApplication = new Microsoft.Office.Interop.Word.Application();
            object objFile = fileName;
            this._wordDocument = _wordApplication.Documents.Open(ref objFile, ref nullobj, ref nullobj,
                ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj,
                ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj);
        }
        /// <summary>
        /// 进行与类创建时指定的文件比较,然后将差异显示在file,并保存退出
        /// </summary>
        /// <param name="file"></param>
        public void Compare(string file)
        {
            object compareTarget = Microsoft.Office.Interop.Word.WdCompareTarget.wdCompareTargetSelected;
            object addToRecentFiles = false;
            _wordDocument.Compare(file, ref nullobj, ref compareTarget, ref nullobj, ref nullobj, ref addToRecentFiles, ref nullobj, ref nullobj);

            object objfile = file;
            this._wordDocument = _wordApplication.Documents.Open(ref objfile, ref nullobj, ref nullobj,
               ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj,
               ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj);
            _wordApplication.NormalTemplate.Saved = true;
            _wordDocument.Save();
            object saveOption = Microsoft.Office.Interop.Word.WdSaveOptions.wdDoNotSaveChanges;
            _wordApplication.Quit(ref saveOption,ref nullobj,ref nullobj);
        }

        /// <summary>
        /// 接受修订
        /// 需重新创建WordHelper类,指定文件
        /// </summary>
        public void AcceptRevised()
        {
            _wordDocument.AcceptAllRevisions();
            _wordDocument.Save();
            object saveOption = Microsoft.Office.Interop.Word.WdSaveOptions.wdDoNotSaveChanges;
            _wordApplication.Quit(ref saveOption, ref nullobj, ref nullobj);
        }
    }
}

 

 

 上面的文字,我看上去我自己很清晰,如果是没有研究过这个问题的朋友看上去一定很像炒面, 但你如果研究过这个问题的话,应该还是可以看懂的!如果有不明白的话,可以留言我们讨论讨论吧!呵

 大概的思路重新写一下:用office组件进行比较,比较的时候,会将不一样的地方使用修订(也有人叫笔记留痕)方式显示在第二个文档里面,然后再使用dsoframer控件加载第二个有修订信息的文档,进行读取.最后再让第二文档接受修订.

大概是这样的咯,不过在真正开发的时候,会遇到很多小问题的,如果遇到的朋友不妨提出来大家讨论讨论..

 

 

上面的三种方法是自己想的,不知有没有更好的方法,知道的朋友不妨通知一声,在下感激不尽..

posted on 2010-11-02 00:14  Ss_Andy  阅读(6831)  评论(4编辑  收藏  举报