2009-06-09 2 views
29

में बाइनरी फ़ाइलों की तुलना करें मैं दो बाइनरी फ़ाइलों की तुलना करना चाहता हूं। उनमें से एक सर्वर पर पहले से गणना की गई सीआरसी 32 के साथ सर्वर पर संग्रहीत है जब से मैंने इसे मूल रूप से संग्रहीत किया था।सी #

मुझे पता है कि अगर सीआरसी अलग है, तो फाइलें निश्चित रूप से अलग हैं। हालांकि, अगर सीआरसी एक जैसा है, तो मुझे नहीं पता कि फाइलें हैं। इसलिए, मैं दो धाराओं की तुलना करने का एक अच्छा प्रभावी तरीका ढूंढ रहा हूं: एक पोस्ट की गई फ़ाइल से और फ़ाइल सिस्टम से एक।

मैं धाराओं पर एक विशेषज्ञ नहीं हूं, लेकिन मुझे अच्छी तरह से पता है कि जहां तक ​​स्मृति उपयोग का संबंध है, मैं आसानी से यहां पैर में खुद को गोली मार सकता हूं।

उत्तर

42
static bool FileEquals(string fileName1, string fileName2) 
{ 
    // Check the file size and CRC equality here.. if they are equal...  
    using (var file1 = new FileStream(fileName1, FileMode.Open)) 
     using (var file2 = new FileStream(fileName2, FileMode.Open)) 
      return FileStreamEquals(file1, file2); 
} 

static bool FileStreamEquals(Stream stream1, Stream stream2) 
{ 
    const int bufferSize = 2048; 
    byte[] buffer1 = new byte[bufferSize]; //buffer size 
    byte[] buffer2 = new byte[bufferSize]; 
    while (true) { 
     int count1 = stream1.Read(buffer1, 0, bufferSize); 
     int count2 = stream2.Read(buffer2, 0, bufferSize); 

     if (count1 != count2) 
      return false; 

     if (count1 == 0) 
      return true; 

     // You might replace the following with an efficient "memcmp" 
     if (!buffer1.Take(count1).SequenceEqual(buffer2.Take(count2))) 
      return false; 
    } 
} 
+3

conunt1 == count2 की आवश्यकता है, स्ट्रीम के रूप में गलत हो सकता है। रीड एक ब्लॉक को वापस करने के लिए स्वतंत्र है जिसमें बाइट गिनती से कम लंबाई है। http://msdn.microsoft.com/en-us/library/vstudio/system.io.stream.read(v=vs.100).aspx – Karata

+0

समाधान मेहरद के समाधान के लिए धन्यवाद। क्या आपको टेक कॉल की आवश्यकता है? मैंने केवल 'कोशिश की है (! Buffer1.SequenceEqual (buffer2))' और ऐसा लगता है कि यह काम करता है। –

+1

@ ओजगुर यह काम करता है लेकिन यह कम कुशल है और बहुत ही अनुशंसित आईएमओ नहीं है। –

3

यदि आप एक SHA1 हस्ताक्षर करने के लिए है कि सीआरसी बदल यह अलग लेकिन एक ही हस्ताक्षर के साथ किया जा रहा है की संभावना astronomicly छोटे

+0

आपको लगता है कि सबसे गंभीर क्षुधा में पर भरोसा नहीं किया जाना चाहिए:

निम्नलिखित कोड तुलना करने के लिए BinaryReader का लाभ लेता है। यह वास्तविक कुंजी की तुलना किए बिना हैशटेबल लुकअप में हैश की जांच करना है! –

+1

दुर्भाग्यवश आप गारंटी दे सकते हैं कि एक बार जब यह गड़बड़ हो जाए तो यह बिल्कुल महत्वपूर्ण होगा, शायद एक बड़ा पिच। –

+0

@ सिमोन - वह बहुत सच है। @ मेहरदाद - शायद नहीं, लेकिन यह सुपर uber सुनिश्चित करने के लिए आपको कितनी बार जांचना होगा, इसे बहुत कम कर देगा। – albertjan

2

तुम भी सीआरसी जाँच संभवतः से बचने के लिए पहले लंबाई और दो फ़ाइलों की तारीखों की जांच कर सकते हैं सीआरसी जांच।

लेकिन अगर आपको पूरी फ़ाइल सामग्री की तुलना करना है, तो मैंने देखा है कि एक साफ चाल सीपीयू के सीधा के बराबर बाइट्स पढ़ रही है। उदाहरण के लिए, 32 बिट पीसी पर, एक समय में 4 बाइट पढ़ें और उन्हें int32 के रूप में तुलना करें। 64 बिट पीसी पर आप एक समय में 8 बाइट्स पढ़ सकते हैं। बाइट द्वारा बाइट करने के रूप में यह लगभग 4 या 8 गुना तेज है। आप शायद एक असुरक्षित कोड ब्लॉक का उपयोग करना चाहते हैं ताकि आप थोड़ा सा स्थानांतरण करने के लिए पॉइंटर्स का उपयोग कर सकें और मूल int आकार में बाइट प्राप्त कर सकें।

आप वर्तमान प्रोसेसर आर्किटेक्चर के आदर्श आकार को निर्धारित करने के लिए IntPtr.Size का उपयोग कर सकते हैं।

+2

क्या आप एक कोड नमूना प्रदान कर सकते हैं कि आप यह कैसे करेंगे? – Svish

20

मैंने पढ़ने स्ट्रीम खंडों पर एक लूप में Int64 तुलना का उपयोग करके "memcmp" को बढ़ाया। यह समय लगभग 1/4 तक कम हो गया।

private static bool StreamsContentsAreEqual(Stream stream1, Stream stream2) 
    { 
     const int bufferSize = 2048 * 2; 
     var buffer1 = new byte[bufferSize]; 
     var buffer2 = new byte[bufferSize]; 

     while (true) 
     { 
      int count1 = stream1.Read(buffer1, 0, bufferSize); 
      int count2 = stream2.Read(buffer2, 0, bufferSize); 

      if (count1 != count2) 
      { 
       return false; 
      } 

      if (count1 == 0) 
      { 
       return true; 
      } 

      int iterations = (int)Math.Ceiling((double)count1/sizeof(Int64)); 
      for (int i = 0; i < iterations; i++) 
      { 
       if (BitConverter.ToInt64(buffer1, i * sizeof(Int64)) != BitConverter.ToInt64(buffer2, i * sizeof(Int64))) 
       { 
        return false; 
       } 
      } 
     } 
    } 
+0

क्या यह फायदेमंद केवल 64-बिट CPUs के लिए है या यह 32-बिट CPUs पर भी मदद करेगा? – Pretzel

+0

इससे कोई फर्क नहीं पड़ता कि आपके पास 32-बिट या 64-बिट ऑपरेटिंग सिस्टम चल रहा है या नहीं। लेकिन मैंने इसे शुद्ध 32-बिट CPU पर कभी भी कोशिश नहीं की। आपको इसे आजमा देना है और शायद इंट 64 को int32 में बदलना है। लेकिन 64-बिट ऑपरेशंस (2004 से x86) में सक्षम कम से कम आधुनिक सीपीयू नहीं हैं? आगे बढिए और इसे आजमाइए! – Lars

+0

[इस उत्तर] पर टिप्पणियां देखें (http://stackoverflow.com/a/968980/157247)। 'Count1' के बराबर' count1' पर भरोसा विश्वसनीय नहीं है। –

6

यह कैसे मैं यह कर लेंगे, जब आप सीआरसी पर भरोसा नहीं करना चाहता था है:

/// <summary> 
    /// Binary comparison of two files 
    /// </summary> 
    /// <param name="fileName1">the file to compare</param> 
    /// <param name="fileName2">the other file to compare</param> 
    /// <returns>a value indicateing weather the file are identical</returns> 
    public static bool CompareFiles(string fileName1, string fileName2) 
    { 
     FileInfo info1 = new FileInfo(fileName1); 
     FileInfo info2 = new FileInfo(fileName2); 
     bool same = info1.Length == info2.Length; 
     if (same) 
     { 
      using (FileStream fs1 = info1.OpenRead()) 
      using (FileStream fs2 = info2.OpenRead()) 
      using (BufferedStream bs1 = new BufferedStream(fs1)) 
      using (BufferedStream bs2 = new BufferedStream(fs2)) 
      { 
       for (long i = 0; i < info1.Length; i++) 
       { 
        if (bs1.ReadByte() != bs2.ReadByte()) 
        { 
         same = false; 
         break; 
        } 
       } 
      } 
     } 

     return same; 
    } 
+1

info2 फ़ाइल नाम 1 के बजाय फ़ाइल नाम 2 को तर्क के रूप में लेना चाहिए। अन्यथा, अच्छा समाधान :-)। – fbastian

0

स्वीकार किए जाते हैं जवाब एक त्रुटि है कि बताया गया है, लेकिन कभी नहीं सुधारा था: धारा कॉल पढ़ अनुरोध किए गए सभी बाइट्स को वापस करने की गारंटी नहीं है।

BinaryReaderReadBytes कॉल के रूप में अनुरोध जब तक धारा के अंत पहले पहुँच जाए रूप में कई बाइट्स वापस जाने के लिए गारंटी है।

static private bool FileEquals(string file1, string file2) 
    { 
     using (FileStream s1 = new FileStream(file1, FileMode.Open, FileAccess.Read, FileShare.Read)) 
     using (FileStream s2 = new FileStream(file2, FileMode.Open, FileAccess.Read, FileShare.Read)) 
     using (BinaryReader b1 = new BinaryReader(s1)) 
     using (BinaryReader b2 = new BinaryReader(s2)) 
     { 
      while (true) 
      { 
       byte[] data1 = b1.ReadBytes(64 * 1024); 
       byte[] data2 = b2.ReadBytes(64 * 1024); 
       if (data1.Length != data2.Length) 
        return false; 
       if (data1.Length == 0) 
        return true; 
       if (!data1.SequenceEqual(data2)) 
        return false; 
      } 
     } 
    }