2009-08-25 24 views
223

我正在開發一個項目,並且必須比較兩個文件,並看看它們是否與激情匹配。使用C#中的語句進行嵌套

我的錯誤檢查和驗證之前很多初稿提出了:

DirectoryInfo di = new DirectoryInfo(Environment.CurrentDirectory + "\\TestArea\\"); 
    FileInfo[] files = di.GetFiles(filename + ".*"); 

    FileInfo outputFile = files.Where(f => f.Extension == ".out").Single<FileInfo>(); 
    FileInfo expectedFile = files.Where(f => f.Extension == ".exp").Single <FileInfo>(); 

    using (StreamReader outFile = new StreamReader(outputFile.OpenRead())) 
    { 
    using (StreamReader expFile = new StreamReader(expectedFile.OpenRead())) 
    { 
     while (!(outFile.EndOfStream || expFile.EndOfStream)) 
     { 
     if (outFile.ReadLine() != expFile.ReadLine()) 
     { 
      return false; 
     } 
     } 
     return (outFile.EndOfStream && expFile.EndOfStream); 
    } 
    } 

這似乎有點奇怪using語句已經嵌套。

有沒有更好的方法來做到這一點?

+2

如果其中一個文件比另一個文件短,但匹配所有它的字節與較大的文件上面的代碼將返回true即使文件不是完全匹配 – 2009-08-25 19:38:08

+0

@Rune FS:良好的發現,修正 – SBurris 2009-08-31 02:06:10

+0

我想我可能已經找到了一個聲明這個使用語句的語法更簡潔的方法,它似乎對我有用?在using語句中使用var而不是IDisposable來使用var類似於允許我實例化兩個對象,並調用它們的屬性和它們所分配的類的方法,如使用(var uow = UnitOfWorkType1(),uow2 = UnitOfWorkType2 ()){} – Caleb 2014-02-21 15:58:08

回答

418

的這樣做的首選方法是在最後的using聲明之後只放一個開口大括號{,如下所示:

using (StreamReader outFile = new StreamReader(outputFile.OpenRead())) 
using (StreamReader expFile = new StreamReader(expectedFile.OpenRead())) 
{ 
    ///... 
} 
+0

今天我學到了東西。您可以使用不同類型的此方法。 +1 – 2009-08-25 17:38:09

+0

是的,當類型不同時,這種方法很好。 – 2009-08-25 17:39:25

+9

清潔劑?也不會強迫你使用相同的類型。即使類型與可讀性和一致性相匹配,我總是這樣做。 – meandmycode 2009-08-25 17:49:34

104

如果對象是同類型,你可以做以下

using (StreamReader outFile = new StreamReader(outputFile.OpenRead()), 
        expFile = new StreamReader(expectedFile.OpenRead())) 
{ 
    // ... 
} 
+1

那麼他們都是相同的類型,如果他們都是IDisposable或許演員會工作? – jpierson 2012-01-17 17:43:24

+4

@jpierson可以工作,是的,但是當你從使用塊中調用IDisposable對象時,我們不能調用任何類成員(沒有強制轉換,這會損害點imo)。 – Connell 2013-03-22 09:58:29

+0

IDisposable是一種類型,因此只是將它用作具有混合類型列表的類型,如其他一些答案中所示。 – 2018-01-13 20:14:58

4

沒有什麼奇怪的了。 using是確保代碼塊完成後丟棄對象的簡便方法。如果你的外部塊有一個可拆卸的物體需要使用內部塊,這是完全可以接受的。

編輯:打字太慢以顯示合併代碼示例。 +1給其他人。

23

IDisposable s爲同一類型的,你可以做到以下幾點:

using (StreamReader outFile = new StreamReader(outputFile.OpenRead()), 
    expFile = new StreamReader(expectedFile.OpenRead()) { 
    // ... 
} 

using的MSDN頁面對這種語言功能的文檔。

你可以做IDisposable S是否是同一類型如下:

using (StreamReader outFile = new StreamReader(outputFile.OpenRead())) 
using (StreamWriter anotherFile = new StreamReader(anotherFile.OpenRead())) 
{ 
    // ... 
} 
3

你可以在一個使用語句組多功能一次性對象之間用逗號:

using (StreamReader outFile = new StreamReader(outputFile.OpenRead()), 
     expFile = new StreamReader(expectedFile.OpenRead())) 
{ 

} 
2

當我編碼時,這些問題也隨時都會出現。你可以考慮將第二個使用語句移到另一個函數中?

4

你可以忽略所有,但括號最內層的使用:

using (StreamReader outFile = new StreamReader(outputFile.OpenRead())) 
using (StreamReader expFile = new StreamReader(expectedFile.OpenRead())) 
{ 
    while (!(outFile.EndOfStream || expFile.EndOfStream)) 
    { 
    if (outFile.ReadLine() != expFile.ReadLine()) 
    { 
     return false; 
    } 
    } 
} 

我覺得這是不是把幾個同類型的在同一使用,如其他人所說清潔,但我肯定很多人會認爲這是令人困惑的

+3

在這種情況下,你可以省略所有的括號... – 2009-08-25 17:55:20

6

你也可以說:

using (StreamReader outFile = new StreamReader(outputFile.OpenRead())) 
using (StreamReader expFile = new StreamReader(expectedFile.OpenRead())) 
{ 
    ... 
} 

但有些人可能會發現難以閱讀。順便說一句,作爲你的問題的一個優化,爲什麼不檢查文件大小是否先行一致?

2

此外,如果您已經知道路徑,掃描目錄沒有意義。

相反,我建議是這樣的:

string directory = Path.Combine(Environment.CurrentDirectory, @"TestArea\"); 

using (StreamReader outFile = File.OpenText(directory + filename + ".out")) 
using (StreamReader expFile = File.OpenText(directory + filename + ".exp"))) 
{ 
    //... 

Path.Combine將一個文件夾或文件名添加到路徑,確保這正是那裏的路徑和名稱之間的一個反斜槓。

File.OpenText將一次打開文件並創建一個StreamReader

的前綴來@的字符串,你可以避免逃脫每反斜槓(例如,@"a\b\c"

8

如果要將文件有效地比較,不要使用StreamReaders可言,然後usings AREN沒有必要 - 您可以使用低級流讀取來拉入數據緩衝區進行比較。

您還可以先比較文件大小等事情,以快速檢測不同的文件,以免自己也必須讀取所有數據。

+0

是的,檢查文件大小是一個好主意,節省你的時間或讀取所有的字節。 (+1) – TimothyP 2009-08-25 17:50:47

2

你還問是否有更好的方法來比較文件? 我更喜歡爲這兩個文件計算一個CRC或MD5並比較它們。

例如,你可以使用下面的擴展方法:

public static class ByteArrayExtender 
    { 
     static ushort[] CRC16_TABLE = { 
         0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241, 
         0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440, 
         0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40, 
         0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841, 
         0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40, 
         0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41, 
         0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641, 
         0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040, 
         0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240, 
         0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441, 
         0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41, 
         0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840, 
         0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41, 
         0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40, 
         0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640, 
         0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041, 
         0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240, 
         0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441, 
         0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41, 
         0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840, 
         0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41, 
         0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40, 
         0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640, 
         0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041, 
         0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241, 
         0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440, 
         0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40, 
         0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841, 
         0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40, 
         0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41, 
         0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641, 
         0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 }; 


     public static ushort CalculateCRC16(this byte[] source) 
     { 
      ushort crc = 0; 

      for (int i = 0; i < source.Length; i++) 
      { 
       crc = (ushort)((crc >> 8)^CRC16_TABLE[(crc^(ushort)source[i]) & 0xFF]); 
      } 

      return crc; 
     } 

一旦你這樣做,這是很容易比較文件:

public bool filesAreEqual(string outFile, string expFile) 
{ 
    var outFileBytes = File.ReadAllBytes(outFile); 
    var expFileBytes = File.ReadAllBytes(expFile); 

    return (outFileBytes.CalculateCRC16() == expFileBytes.CalculateCRC16()); 
} 

你可以使用內置的System.Security .Cryptography.MD5類, 但計算的散列是一個字節[],所以你仍然必須比較這兩個數組。如果你不

using (StreamReader outFile = new StreamReader(outputFile.OpenRead())) 
    using (StreamReader expFile = new StreamReader(expectedFile.OpenRead())) 
    while (!(outFile.EndOfStream || expFile.EndOfStream)) 
     if (outFile.ReadLine() != expFile.ReadLine())  
      return false; 
+2

而不是採取一個字節數組,方法應該採取一個'Stream'對象並調用'ReadByte'方法直到它返回-1。這將爲大文件節省大量內存。 – SLaks 2009-08-25 17:53:49

+0

然後你會如何計算所有字節的crc? – TimothyP 2009-08-25 17:56:53

+0

哦,不要緊,我說:p thnx,我會改變它在我的代碼:p 我們只用它的數據<1000字節,所以還沒有發現問題,但將改變 – TimothyP 2009-08-25 18:00:16

4

而只是添加到清晰,在這種情況下,由於每個連續的語句是一條語句,(而不是塊),則可以忽略所有的括號不要在using塊之前聲明你的使用塊的變量,你可以在同一個using語句中聲明它們。

Test t; 
    Blah u; 
    using (IDisposable x = (t = new Test()), y = (u = new Blah())) { 
     // whatever... 
    } 

這樣,x和y只是使用塊的IDisposable類型的佔位符變量,您可以在代碼中使用t和u。只是想我會提到。

+0

有趣的解決方案;這樣做/甚至使用最低級別的一組括號,可以實現與將它們左對齊(更清潔的IMO)堆疊在一起的相同目標,同時解決其他人提到的表現任何從屬性的裝飾性嵌套願望。 – user1172173 2017-10-29 14:55:12

13

+0

我覺得這會讓看到你的代碼的新開發者感到困惑。 – Zack 2015-01-06 14:40:18

+0

這可能是一個不好的做法;它有一個副作用,即使非託管資源被釋放後變量仍然存在。根據微軟的C#參考資料,「你可以實例化資源對象,然後將該變量傳遞給using語句,但這不是最佳實踐,在這種情況下,控制離開使用塊後,對象仍然在範圍內,即使它會可能不再能夠訪問其非託管資源。「 – 2015-08-18 13:30:56

+0

@RobertAltman你是對的,在真實的代碼中,我會使用另一種方法(可能來自Gavin H)。這只是一個不太可取的選擇。 – Botz3000 2015-08-19 07:01:45

7

using語句處理IDisposable接口,所以另一個選項可能是創建某種類型的組合類,它實現了IDisposable並引用了您通常放入使用語句中的所有IDisposable對象。不利的一面是,你必須首先聲明你的變量,並且在範圍之外聲明你的變量,以便它們在需要更多代碼行的使用塊內有用,而不是其他一些建議所需要的。

Connection c = new ...; 
Transaction t = new ...; 

using (new DisposableCollection(c, t)) 
{ 
    ... 
} 

爲DisposableCollection的構造是在這種情況下,一個參數數組,因此你可以在你喜歡的很多。

2

我想我可能已經找到了一個聲明這個使用語句的語法更清晰的方法,它似乎適用於我?在using語句中使用var而不是IDisposable似乎可以動態地推斷兩個對象的類型,並允許我實例化兩個對象並調用它們的屬性和它們分配給的類的方法,如

using(var uow = new UnitOfWorkType1(), uow2 = new UnitOfWorkType2()){}.


如果有人知道爲什麼這是不正確的,請讓我知道

+1

如果所有的東西都屬於同一類型,則可以在同一條線上運行。混合類型必須分開使用()s。但它不適用於var,你必須指定一個類型(C#5規範,p237) – 2014-11-27 17:03:44