Практическое руководство. Сравнение содержимого двух папок (LINQ)
Обновлен: Ноябрь 2007
Данный пример демонстрирует три способа сравнения двух списков файлов.
Путем запроса логического значения, указывающего, являются ли два списка файлов идентичными.
Путем запроса пересечения для извлечения файлов, находящихся в обеих папках.
Путем запроса разности наборов для извлечения файлов, находящихся в одной папке, но отсутствующих в другой.
Примечание.
Методы, представленные в этом примере, могут быть адаптированы для сравнения последовательностей объектов любого типа.
Класс FileComparer, показанный здесь, демонстрирует использование пользовательского класса сравнения вместе со стандартными операторами запросов. Класс не предназначен для использования в реальных сценариях. Он использует только имя и длину каждого файла в байтах для определения идентичности содержимого папок. В реальном сценарии для выполнения более строгой проверки равенства этот класс следует изменить.
Пример
Module CompareDirs
Public Sub Main()
' Create two identical or different temporary folders
' on a local drive and add files to them.
' Then set these file paths accordingly.
Dim pathA As String = "C:\TestDir"
Dim pathB As String = "C:\TestDir2"
' Take a snapshot of the file system.
Dim list1 = GetFiles(pathA)
Dim list2 = GetFiles(pathB)
' Create the FileCompare object we'll use in each query
Dim myFileCompare As New FileCompare
' This query determines whether the two folders contain
' identical file lists, based on the custom file comparer
' that is defined in the FileCompare class.
' The query executes immediately because it returns a bool.
Dim areIdentical As Boolean = list1.SequenceEqual(list2, myFileCompare)
If areIdentical = True Then
Console.WriteLine("The two folders are the same.")
Else
Console.WriteLine("The two folders are not the same.")
End If
' Find common files in both folders. It produces a sequence and doesn't execute
' until the foreach statement.
Dim queryCommonFiles = list1.Intersect(list2, myFileCompare)
If queryCommonFiles.Count() > 0 Then
Console.WriteLine("The following files are in both folders:")
For Each fi As System.IO.FileInfo In queryCommonFiles
Console.WriteLine(fi.FullName)
Next
Else
Console.WriteLine("There are no common files in the two folders.")
End If
' Find the set difference between the two folders.
' For this example we only check one way.
Dim queryDirAOnly = list1.Except(list2, myFileCompare)
Console.WriteLine("The following files are in dirA but not dirB:")
For Each fi As System.IO.FileInfo In queryDirAOnly
Console.WriteLine(fi.FullName)
Next
' Keep the console window open in debug mode
Console.WriteLine("Press any key to exit.")
Console.ReadKey()
End Sub
' This implementation defines a very simple comparison
' between two FileInfo objects. It only compares the name
' of the files being compared and their length in bytes.
Public Class FileCompare
Implements System.Collections.Generic.IEqualityComparer(Of System.IO.FileInfo)
Public Function Equals1(ByVal x As System.IO.FileInfo, ByVal y As System.IO.FileInfo) _
As Boolean Implements System.Collections.Generic.IEqualityComparer(Of System.IO.FileInfo).Equals
If (x.Name = y.Name) And (x.Length = y.Length) Then
Return True
Else
Return False
End If
End Function
' Return a hash that reflects the comparison criteria. According to the
' rules for IEqualityComparer(Of T), if Equals is true, then the hash codes must
' also be equal. Because equality as defined here is a simple value equality, not
' reference identity, it is possible that two or more objects will produce the same
' hash code.
Public Function GetHashCode1(ByVal fi As System.IO.FileInfo) _
As Integer Implements System.Collections.Generic.IEqualityComparer(Of System.IO.FileInfo).GetHashCode
Dim s As String = fi.Name & fi.Length
Return s.GetHashCode()
End Function
End Class
' Function to retrieve a list of files. Note that this is a copy
' of the file information.
Function GetFiles(ByVal root As String) As IEnumerable(Of System.IO.FileInfo)
Return From file In My.Computer.FileSystem.GetFiles _
(root, FileIO.SearchOption.SearchAllSubDirectories, "*.*") _
Select New System.IO.FileInfo(file)
End Function
End Module
namespace QueryCompareTwoDirs
{
class CompareDirs
{
static void Main(string[] args)
{
// Create two identical or different temporary folders
// on a local drive and change these file paths.
string pathA = @"C:\TestDir";
string pathB = @"C:\TestDir2";
// Take a snapshot of the file system.
IEnumerable<System.IO.FileInfo> list1 = GetFiles(pathA);
IEnumerable<System.IO.FileInfo> list2 = GetFiles(pathB);
//A custom file comparer defined below
FileCompare myFileCompare = new FileCompare();
// This query determines whether the two folders contain
// identical file lists, based on the custom file comparer
// that is defined in the FileCompare class.
// The query executes immediately because it returns a bool.
bool areIdentical = list1.SequenceEqual(list2, myFileCompare);
if (areIdentical == true)
{
Console.WriteLine("the two folders are the same");
}
else
{
Console.WriteLine("The two folders are not the same");
}
// Find the common files. It produces a sequence and doesn't
// execute until the foreach statement.
var queryCommonFiles = list1.Intersect(list2, myFileCompare);
if (queryCommonFiles.Count() > 0)
{
Console.WriteLine("The following files are in both folders:");
foreach (var v in queryCommonFiles)
{
Console.WriteLine(v.FullName); //shows which items end up in result list
}
}
else
{
Console.WriteLine("There are no common files in the two folders.");
}
// Find the set difference between the two folders.
// For this example we only check one way.
var queryList1Only = (from file in list1
select file).Except(list2, myFileCompare);
Console.WriteLine("The following files are in list1 but not list2:");
foreach (var v in queryList1Only)
{
Console.WriteLine(v.FullName);
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
// This method assumes that the application has discovery
// permissions for all folders under the specified path.
static IEnumerable<System.IO.FileInfo> GetFiles(string path)
{
if (!System.IO.Directory.Exists(path))
throw new System.IO.DirectoryNotFoundException();
string[] fileNames = null;
List<System.IO.FileInfo> files = new List<System.IO.FileInfo>();
fileNames = System.IO.Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories);
foreach (string name in fileNames)
{
files.Add(new System.IO.FileInfo(name));
}
return files;
}
}
// This implementation defines a very simple comparison
// between two FileInfo objects. It only compares the name
// of the files being compared and their length in bytes.
class FileCompare : System.Collections.Generic.IEqualityComparer<System.IO.FileInfo>
{
public FileCompare() { }
public bool Equals(System.IO.FileInfo f1, System.IO.FileInfo f2)
{
return (f1.Name == f2.Name &&
f1.Length == f2.Length);
}
// Return a hash that reflects the comparison criteria. According to the
// rules for IEqualityComparer<T>, if Equals is true, then the hash codes must
// also be equal. Because equality as defined here is a simple value equality, not
// reference identity, it is possible that two or more objects will produce the same
// hash code.
public int GetHashCode(System.IO.FileInfo fi)
{
string s = String.Format("{0}{1}", fi.Name, fi.Length);
return s.GetHashCode();
}
}
}
Компиляция кода
Создайте проект Visual Studio, предназначенный для платформы .NET Framework версии 3.5. По умолчанию в этом проекте имеется ссылка на файл System.Core.dll и директива using (C#) или оператор Imports (Visual Basic) для пространства имен System.Linq. При работе с проектами C# добавьте директиву using для пространства имен System.IO.
Скопируйте код в созданный проект.
Нажмите клавишу F5, чтобы скомпилировать и запустить программу.
Нажмите любую клавишу для выхода из окна консоли.
Отказоустойчивость
Для интенсивного использования операций запроса к содержимому нескольких типов документов и файлов, рассмотрите возможность использования средства поиска Windows Desktop Search.