如何使用字节数组和哈希比较文件

时间:2016-07-20 20:05:55

标签: c# arrays hash

背景

我正在将媒体文件转换为新格式,并且需要一种方法来了解我之前在当前运行时是否转换过文件。

我的解决方案

散列每个文件并将散列存储在数组中。每次我转换文件时,我都会对其进行哈希处理,并根据存储在数组中的哈希检查哈希值。

问题

我的逻辑似乎无法检测到我已经看过文件的时间,并且我最终会多次转换同一个文件。

代码

//Byte array of already processed files
 private static readonly List<byte[]> Bytelist = new List<byte[]>();      

        public static bool DoCheck(string file)
        {
            FileInfo info = new FileInfo(file);

            while (FrmMain.IsFileLocked(info)) //Make sure file is finished being copied/moved
            {
                Thread.Sleep(500);
            }

            //Get byte sig of file and if seen before dont process
            byte[] myFileData = File.ReadAllBytes(file);
            byte[] myHash = MD5.Create().ComputeHash(myFileData);

            if (Bytelist.Count != 0)
            {
                foreach (var item in Bytelist)
                {
                    //If seen before ignore
                    if (myHash == item)
                    {
                        return true;
                    }
                }
            }
            Bytelist.Add(myHash);
            return false;
        }

问题

是否有更有效的方法来实现我的最终目标?我做错了什么?

4 个答案:

答案 0 :(得分:2)

有很多问题,我将回答第一个问题:

  

是否有更有效的方法来实现我的最终目标?

TL; DR是的。

您正在存储哈希值并仅为文件比较哈希值,这是一项非常昂贵的操作。您可以在计算哈希值之前进行其他检查:

  1. 文件大小是否相同?如果没有,请转到下一个检查。
  2. 第一堆字节是否相同?如果没有,请转到下一个检查。
  3. 此时你必须检查哈希值(MD5)。
  4. 当然,您必须为每个已处理的文件存储大小/前X个字节/哈希值。

    此外,同样的 MD5 并不意味着文件是相同的,所以您可能需要采取额外的步骤来检查它们是否真的相同,但这可能是一种过度杀伤,取决于重新处理文件的成本有多大,可能更重要的是不计算昂贵的哈希值。

    编辑:第二个问题:可能会失败,因为您要比较两个字节数组的引用,这些引用永远不会与您每次创建新数组相同,您需要创建序列相等 byte [] 之间的比较。 (或者将哈希值转换为字符串并比较字符串)

    var exists = Bytelist.Any(hash => hash.SequenceEqual(myHash));
    

答案 1 :(得分:1)

  • 您确定此新文件格式无法添加额外的元数据 内容?比如上次修改,还是改变的属性?
  • 此外,如果您要转换为已知格式,那么应该有一个 使用文件签名的方式来知道它是否已经采用这种格式或 不是,如果这是你的格式,那么添加一些额外的字节用于签名以识别它。
  • 不要忘记,如果您的应用关闭并再次打开,它会 通过您的方法再次重新访问所有文件。
  • 关于代码的另一个最后一点,我不喜欢存储字节 数组,但如果你应该,你创建HashSet会更好 它不是列表,而是访问时间为O(1)。

答案 2 :(得分:1)

在效率,效果和风格方面还有很大的改进空间,但这不是CodeReview.SE,所以我会尝试解决手头的问题:

您使用==运算符检查两个字节的数组是否相等。但这只会执行引用相等测试 - 即测试两个变量是否指向同一实例,即同一个数组。当然,这不会在这里工作。

有很多方法可以做到这一点,从数组上的简单foreach循环开始(可能是首先检查长度的优化)或使用Enumerable.SequenceEquals,如Convert.ToBase64String所示。 3}}

更好的是,将哈希的byte []转换为字符串(任何字符串 - private static readonly HashSet<string> _computedHashes = new HashSet<string>(); public static bool DoCheck(string file) { /// stuff //Get byte sig of file and if seen before dont process byte[] myFileData = File.ReadAllBytes(file); byte[] myHash = MD5.Create().ComputeHash(myFileData); string hashString = Convert.ToBase64String(myHash); return _computedHashes.Contains(hashString); } 都是一个不错的选择)并将 存储在Bytelist缓存中(应该是Hashset,而不是List。字符串针对这些比较进行了优化,您不会遇到&#34;参考相等&#34;问题在这里。

所以样本解决方案就是这样:

{{1}}

据推测,您在完成转换后将哈希添加到_computedHashes集。

答案 3 :(得分:0)

您必须逐项比较字节数组:

foreach (var item in Bytelist)
                {
                    //If seen before ignore
                    if (myHash.Length == item.Length)
                    {
                        bool isequal = true;
                        for (int i = 0; i < myHash.Length; i++)
                        {
                            if (myHash[i] != item[i])
                            {
                                isequal = false;
                            }
                        }
                        if (isequal)
                        {
                            return true;
                        }

                    }                   
                }