【问题标题】:Comparing two excel files for differences比较两个excel文件的差异
【发布时间】:2013-08-26 20:40:56
【问题描述】:

我想比较两个输入的 csv 文件,看看是否有添加或删除的行。解决此问题的最佳方法是什么。我没有使用列名,因为所有文件的列名不一致。

private void compare_btn_Click(object sender, EventArgs e)
        {
            string firstFile = firstExcel_txt.Text;
            var results = ReadExcel(openFileDialog1);
            string secondFile = secondExcel_txt.Text;
            var results2 = ReadExcel(openFileDialog2);

        }

阅读:

public object ReadExcel(OpenFileDialog openFileDialog)
        {
            var _excelFile = new ExcelQueryFactory(openFileDialog.FileName);
            var _info = from c in _excelFile.WorksheetNoHeader() select c;
            string header1, header2, header3;
            foreach (var item in _info)
            {
                header1 = item.ElementAt(0);
                header2 = item.ElementAt(1);
                header3 = item.ElementAt(2);
            }
            return _info;
        }

任何关于我如何做到这一点的帮助都会很棒。

【问题讨论】:

  • 最好和最准确的方法是将它们都转换为字节数组,并在它们都转换时进行比较。以下链接将帮助您将 excel 工作表转换为字节数组:c-sharpcorner.com/UploadFile/1a81c5/…
  • Masriyah 你只有 3 列或者你只是简化了你的代码?我没有看到您将 excel 文件的内容保存在哪里进行比较
  • 或者您可以丢弃列并对其余列进行哈希处理。如果两个文件的哈希值匹配,则它们具有相同的数据,逐字逐句。根据所使用的算法,哈希冲突的可能性很小,但它是如此之小,以至于在你发生冲突之前地狱会冻结。
  • @MauricioGracia 是的,我为这篇文章简化了列 - 如何保留 excel 文件的内容?
  • @Masriyah 我刚刚添加并回答了显示存储和比较 excel 文件文本内容的方法

标签: c# winforms linq excel csv


【解决方案1】:

我建议你为excel文件的每一行计算一个哈希,然后你可以继续比较每一行的哈希,看看它是否与另一个文件上的任何哈希匹配(见源代码中的 cmets)

我还提供了一些类来存储您的 Excel 文件的内容

using System.Security.Cryptography;

private void compare_btn_Click(object sender, EventArgs e)
{
    string firstFile = firstExcel_txt.Text;
    ExcelInfo file1 = ReadExcel(openFileDialog1);

    string secondFile = secondExcel_txt.Text;
    ExcelInfo file2 = ReadExcel(openFileDialog2);

    CompareExcels(file1,file2) ;
}    

public void CompareExcels(ExcelInfo fileA, ExcelInfo fileB)
{
    foreach(ExcelRow rowA in fileA.excelRows)
    {
        //If the current hash of a row of fileA does not exists in fileB then it was removed 
        if(! fileB.ContainsHash(rowA.hash))
        {
            Console.WriteLine("Row removed" + rowA.ToString());
        }
    }

    foreach(ExcelRow rowB in fileB.excelRows)
    {
        //If the current hash of a row of fileB does not exists in fileA then it was added 
        if(! fileA.ContainsHash(rowB.hash))
        {
            Console.WriteLine("Row added" + rowB.ToString());
        }
    }
}

public Class ExcelRow
{
    public List<String> lstCells ;
    public byte[] hash

    public ExcelRow()
    {
        lstCells = new List<String>() ;
    }
    public override string ToString()
    {
        string resp ;

        resp = string.Empty ;

        foreach(string cellText in lstCells)
        {
            if(resp != string.Empty)
            {
                resp = resp + "," + cellText ;
            }
            else
            {
                resp = cellText ;
            }   
        }
        return resp ;
    }       
    public void CalculateHash()
    {
        byte[] rowBytes ;
        byte[] cellBytes ;
        int pos ;
        int numRowBytes ;

        //Determine how much bytes are required to store a single excel row
        numRowBytes = 0 ;
        foreach(string cellText in lstCells)
        {
            numRowBytes += NumBytes(cellText) ;
        }       

        //Allocate space to calculate the HASH of a single row

        rowBytes= new byte[numRowBytes]
        pos = 0 ;

        //Concatenate the cellText of each cell, converted to bytes,into a single byte array
        foreach(string cellText in lstCells)
        {
            cellBytes = GetBytes(cellText) ;
            System.Buffer.BlockCopy(cellBytes, 0, rowBytes, pos, cellBytes.Length);
            pos = cellBytes.Length ;

        }

        hash = new MD5CryptoServiceProvider().ComputeHash(rowBytes);

    }
    static int NumBytes(string str)
    {
        return str.Length * sizeof(char);
    }

    static byte[] GetBytes(string str)
    {
        byte[] bytes = new byte[NumBytes(str)];
        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
    }
}
public Class ExcelInfo
{
    public List<ExcelRow> excelRows ;

    public ExcelInfo()
    {
        excelRows = new List<ExcelRow>();
    }
    public bool ContainsHash(byte[] hashToLook)
    {
        bool found ;

        found = false ;

        foreach(ExcelRow eRow in excelRows)
        {
            found = EqualHash(eRow.hash, hashToLook) ;

            if(found)
            {
                break ;
            }
        }

        return found ;
    }
    public static EqualHash(byte[] hashA, byte[] hashB)
    {
        bool bEqual ;
        int i ;

        bEqual  = false;
        if (hashA.Length == hashB.Length)
        {
            i = 0;
            while ((i < hashA.Length) && (hashA[i] == hashB[i]))
            {
                i++ ;
            }
            if (i == hashA.Length)
            {
                bEqual = true;
            }
        }
        return bEqual ;
    }
}

public ExcelInfo ReadExcel(OpenFileDialog openFileDialog)
{
    var _excelFile = new ExcelQueryFactory(openFileDialog.FileName);
    var _info = from c in _excelFile.WorksheetNoHeader() select c;

    ExcelRow excelRow ;
    ExcelInfo resp ;

    resp = new ExcelInfo() ;

    foreach (var item in _info)
    {
        excelRow = new ExcelRow() ;

        //Add all the cells (with a for each)
        excelRow.lstCells.Add(item.ElementAt(0));
        excelRow.lstCells.Add(item.ElementAt(1));
        ....
        //Add the last cell of the row
        excelRow.lstCells.Add(item.ElementAt(N));

        //Calculate the hash of the row
        excelRow.CalculateHash() ;

        //Add the row to the ExcelInfo object
        resp.excelRows.Add(excelRow) ;
    }
    return resp ;
}

【讨论】:

  • 对于我在 ReadExcel 方法 return _info 中的返回,它抛出了一个错误,我缺少一个演员并且无法从 linq IQuerable 转换为 ExcelInfo (ExcelFileReader)。
  • @Masriyah 抱歉,您需要“返回响应;”
  • 当我运行 excelRow.lstCells.Add(item.ElementAt(0)) 时,我的 ReadExcel 方法中出现空引用异常和错误消息:Object reference not set to an instance of an object.
  • @Masriyah 我贴的代码没有编译,很快用notepad++写的,很高兴知道主要思想有帮助
  • 没问题 - 在计算哈希方法中比较两个文件时,有没有一种方法可以实际显示已删除或添加的记录?
【解决方案2】:

最准确的方法是将它们都转换为byte arrays,当两者都转换为数组时检查差异,使用以下链接获取有关如何转换excel的简单示例表格byte arrays

Convert Excel to Byte[]

现在您已将两个 Excel 工作表都转换为字节 [],您应该通过检查字节数组是否相等来检查它们是否存在差异。

可以使用linq通过以下几种方式进行检查:

using System.Linq; //SequenceEqual

 byte[] FirstExcelFileBytes = null;
 byte[] SecondExcelFileBytes = null;

 FirstExcelFileBytes = GetFirstExcelFile();
 SecondExcelFileBytes = GetSecondExcelFile();

 if (FirstExcelFileBytes.SequenceEqual<byte>(SecondExcelFileBytes) == true)
 {
      MessageBox.Show("Arrays are equal");
 }
 else
 {
     MessageBox.Show("Arrays don't match");
 }

还有很多其他方法可以比较字节数组,你应该研究一下哪种方法最适合你。

使用以下链接检查Row addedrow removed 等内容。

Compare excelsheets

【讨论】:

  • 我相信这会很有帮助。我正在更多地考虑返回诸如添加或删除行之类的内容-这可能吗?
  • 帮助链接是为了实现这一点,比较字节数组将返回真或假
  • 这种方法并不总是适用于 xlsx。 XLSX 文件是包含修改日期的 Zip 文件。这就是为什么可以有 2 个不同的二进制文件数组,但它们仍然代表相同的内容
猜你喜欢
  • 1970-01-01
  • 2021-12-11
  • 2014-06-08
  • 2021-06-30
  • 2019-05-15
  • 1970-01-01
  • 1970-01-01
  • 2017-03-08
  • 2021-10-03
相关资源
最近更新 更多