顯示兩個文件不一致的內容的實現(細胞分裂的方法)

       對於相關的實現前人已經做得很好了,winmerge就是其中非常成功的軟件。筆者重新實現只是因爲winmerge的報表功能不能提供我需要的形式的報表。對此,也處於興趣,按照自己的比較算法實現了一個這樣的功能。

    先簡述一下算法思路,這個算法雖說是尋找不一致的內容,但算法的實習是以儘可能找到對應的一致內容爲基礎。剝除一致的內容,剩下的就是不一致的內容。算法存在這樣的一個假設,比較的兩個文件目標文件只有少量是不一樣的,大部分都是一致的。更進一步說,文件B是在文件A的基礎上進行的修改。其修內容可能是在某個位置加了一行,刪除了一行,或者修改了某一行等等。這個假設並不過分,因爲如果比較兩個面目全非的文件,比較的結果並沒有參考價值。現在用個簡單的例子描述下比較過程。

字符串1:AABBCCDDEEFF

字符串2:AABBBDCDDEEFFG

 

步驟1:

爲了得字符串1和字符串1不一致之處,第一步先匹配出最大一致的字符串。這裏一致的字符串有

AABB ,CDDEEFF,暫且稱爲"可能對應組",這裏有兩個對應組。(這裏的一致,表示從字符串1的某個位置開始的一段距離和字符串2完全一致。它們存在可能對應的關係,如果真的存在對應,稱爲"真實對應組",它表明這個組的字符串是原本沒有被修改過的地方)通常"可能對應組"不僅有一個,我們認定字符串長度最長的"可能對應組",是"真實對應組",這裏是CDDEEFF。將"真實對應組"分別從兩個字符串取出。 我們得到一下的一組比較值。

 

字符串1:AABBC     (CDDEEFF)      null

字符串2:AABBBD   (CDDEEFF)      G  

括號代表"真實對應組",已經去除的部分,null代表空字符串。去除後兩個字符串,分裂成了四個字符串。四個字符串亮亮之間存在空間的對應關係。也就是修改前和修改後的關係。我們把字符串1.1和字符串2.1稱之爲"對應塊"1,我們把字符串1.2和字符串2.2稱爲"對應塊"2.

字符串1.1:AABBC         字符串1.2:null

字符串2.1:AABBBD       字符串2.2: G  

 

步驟2:按照步驟1的方法比較每個"對應塊"。分裂成四個"對應塊"

......重複以上步驟。

 

最終結果是已經不存在"真實對應組"。現在每個"對應塊"就是文件不一致的部分。這裏爲了說明用字符串來比較,如果是比較文件應該會用行來比較。A可能代表的是若干行.

給出.net代碼的實現
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace CompareFile
{
    public class CompareFile
    {
        public string output = string.Empty;
        public string dir1 = string.Empty;
        public string dir2 = string.Empty;
        public OutPutBase OutTool = null;

        //入口函數
        public void DoCompareFile()
        {
            DoCompareFile(null);
        }
        //比較的文件列表
        public void DoCompareFile(string[] files)
        {
            if (files == null || files.Length == 0)
            {
                if (output != string.Empty && dir1 != string.Empty && dir2 != string.Empty)
                    CheckDir(new DirectoryInfo(dir1));
            }
            else
            {
                CheckFiles(files);
            }
        }

        //比較目標文件夾
        private void CheckDir(DirectoryInfo dir)
        {
            if (OutTool == null)
                OutTool = new OutPutBase();

            OutTool.outFile = output;
            foreach (FileInfo file in dir.GetFiles())
            {
                if (file.Extension == ".cs" || file.Extension == ".aspx.cs" || file.Extension == ".aspx")
                {
                    System.Console.WriteLine(file.Name);
                    string Tem = dir.FullName.Replace(dir1, dir2);
                    string File2 = Tem + "//" + file.Name;
                    CompareList(file.FullName, GetFileList(file.FullName), GetFileList(File2));
                }
            }
            foreach (DirectoryInfo d in dir.GetDirectories())
            {
                CheckDir(d);
            }

        }

        //比較文件列表
        private void CheckFiles(string[] files)
        {
            if (OutTool == null)
                OutTool = new OutPutBase();

            OutTool.outFile = output;
            foreach (string fileName in files)
            {
                FileInfo file = new FileInfo(fileName);
                //這裏過濾了一下文件類型,實際的應用可以根據需要定義類型
                if (file.Extension == ".cs" || file.Extension == ".aspx.cs" || file.Extension == ".aspx")
                {
                    System.Console.WriteLine(file.Name);
                    string File2 = file.FullName.Replace(dir1, dir2);
                    if (File.Exists(File2))
                    {
                        CompareList(file.FullName, GetFileList(file.FullName), GetFileList(File2));
                    }
                }
            }

        }

        //讀取文件形成字符串行集合
        List<string> GetFileList(string FileName)
        {
            List<string> FileList = new List<string>();
            using (StreamReader sw = new StreamReader(FileName, System.Text.Encoding.Default))
            {
                string str = null;
                while ((str = sw.ReadLine()) != null)
                {
                    FileList.Add(str);
                }
            }
            return FileList;
        }
        //比較文件行集合
        void CompareList(string FileName, List<string> FileList1, List<string> FileList2)
        {
            List<string> SameList = new List<string>();

            CompareResult cr = FindMostLen(FileList1, FileList2);
            //結果輸出,這裏用輸出CSV的方式,此處可由讀者自行實現
            //最大長度爲0代表已經不存在真實對應組
            if (cr.mostLen == 0) OutTool.WriteList(FileName, cr.file2.downFile, cr.file1.downFile);
            else
            {
                CompareList(FileName, cr.file1.upFile, cr.file2.upFile);
                CompareList(FileName, cr.file1.downFile, cr.file2.downFile);
            }

        }
        //自定義類存放比較結果
        class CompareResult
        {
            public int mostLen = 0;
            public result file1 = new result();
            public result file2 = new result();
        }

        //自定義類存放文件塊
        class result
        {
            public int mostStart = 0;
            public int mostEnd = 0;
            public List<string> upFile = new List<string>();
            public List<string> downFile = new List<string>();

            public void SetFile(List<string> file)
            {

                upFile = file.GetRange(0, mostStart);
                downFile = file.GetRange(mostEnd, file.Count - mostEnd);
            }
        }

        //尋找真實對應塊的過程
        CompareResult FindMostLen(List<string> FileList1, List<string> FileList2)
        {
            CompareResult compareResult = new CompareResult();
            for (int i = 0; i < FileList1.Count; i++)
            {
                int len = 0;
                int start = FileList2.IndexOf(FileList1[i]);
                if (start == -1)
                {
                    continue;
                }
                else
                {
                    int flag = i;
                    int j = start;
                    for (; j < FileList2.Count; j++)
                    {
                        if (flag < FileList1.Count)
                        {
                            if (FileList2[j] == FileList1[flag])
                            {
                                len++;
                                flag++;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    if (i + len != flag) throw new Exception("err");

                    if (len > compareResult.mostLen)
                    {
                        compareResult.mostLen = len;
                        compareResult.file1.mostStart = i;
                        compareResult.file1.mostEnd = flag;
                        compareResult.file2.mostStart = start;
                        compareResult.file2.mostEnd = j;
                    }

                    i = flag;

                }
            }

            compareResult.file1.SetFile(FileList1);
            compareResult.file2.SetFile(FileList2);

            return compareResult;
        }
    }
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章