Nasıl yapılır: Dosyaları ve dizinleri sorgulamak için LINQ kullanma

Birçok dosya sistemi işlemi temelde sorgulardır ve bu nedenle LINQ yaklaşımına çok uygundur. Bu sorgular yok edici değildir. Özgün dosya veya klasörlerin içeriğini değiştirmezler. Sorgular herhangi bir yan etkiye neden olmamalıdır. Genel olarak, kaynak verileri değiştiren tüm kodlar (oluşturma/güncelleştirme/silme işlemleri gerçekleştiren sorgular dahil) yalnızca verileri sorgulayan koddan ayrı tutulmalıdır.

Dosya sisteminin içeriğini doğru bir şekilde temsil eden ve özel durumları düzgün bir şekilde işleyen bir veri kaynağı oluşturmanın karmaşıklığı vardır. Bu bölümdeki örnekler, belirtilen kök klasör altındaki tüm dosyaları ve tüm alt klasörlerini temsil eden nesnelerin anlık görüntü koleksiyonunu FileInfo oluşturur. Her FileInfo birinin gerçek durumu, bir sorguyu yürütmeye başladığınız ve bitirdiğiniz zaman arasında değişebilir. Örneğin, veri kaynağı olarak kullanılacak nesnelerin listesini FileInfo oluşturabilirsiniz. Bir sorguda özelliğine Length erişmeye çalışırsanız, FileInfo nesnesi değerini Lengthgüncelleştirmek için dosya sistemine erişmeye çalışır. Dosya artık yoksa, doğrudan dosya sistemini sorgulamasanız bile sorgunuza bir FileNotFoundException alırsınız.

Belirli bir öznitelik veya ada sahip dosyaları sorgulama

Bu örnekte, belirtilen bir dizin ağacında belirtilen dosya adı uzantısına (örneğin ".txt") sahip tüm dosyaların nasıl bulunduğunu gösterir. Ayrıca, oluşturma zamanına göre ağaçtaki en yeni veya en eski dosyanın nasıl döndürüleceği de gösterilir. İster Windows, Mac ister Linux sisteminde bu kodu çalıştırıyor olun, örneklerin çoğunun ilk satırını değiştirmeniz gerekebilir.

string startFolder = """C:\Program Files\dotnet\sdk""";
// Or
// string startFolder = "/usr/local/share/dotnet/sdk";

DirectoryInfo dir = new DirectoryInfo(startFolder);
var fileList = dir.GetFiles("*.*", SearchOption.AllDirectories);

var fileQuery = from file in fileList
                where file.Extension == ".txt"
                orderby file.Name
                select file;

// Uncomment this block to see the full query
// foreach (FileInfo fi in fileQuery)
// {
//    Console.WriteLine(fi.FullName);
// }

var newestFile = (from file in fileQuery
                  orderby file.CreationTime
                  select new { file.FullName, file.CreationTime })
                  .Last();

Console.WriteLine($"\r\nThe newest .txt file is {newestFile.FullName}. Creation time: {newestFile.CreationTime}");

Dosyaları uzantıya göre gruplandırma

Bu örnekte, LINQ'in dosya veya klasör listelerinde gelişmiş gruplandırma ve sıralama işlemleri gerçekleştirmek için nasıl kullanılabileceğini gösterir. Ayrıca ve Take yöntemlerini kullanarak konsol penceresinde çıkışın nasıl sayfaya alındığı Skip da gösterilir.

Aşağıdaki sorgu, belirtilen dizin ağacının içeriğini dosya adı uzantısına göre gruplandırma işlemini gösterir.

string startFolder = """C:\Program Files\dotnet\sdk""";
// Or
// string startFolder = "/usr/local/share/dotnet/sdk";

int trimLength = startFolder.Length;

DirectoryInfo dir = new DirectoryInfo(startFolder);

var fileList = dir.GetFiles("*.*", SearchOption.AllDirectories);

var queryGroupByExt = from file in fileList
                      group file by file.Extension.ToLower() into fileGroup
                      orderby fileGroup.Count(), fileGroup.Key
                      select fileGroup;

// Iterate through the outer collection of groups.
foreach (var filegroup in queryGroupByExt.Take(5))
{
    Console.WriteLine($"Extension: {filegroup.Key}");
    var resultPage = filegroup.Take(20);

    //Execute the resultPage query
    foreach (var f in resultPage)
    {
        Console.WriteLine($"\t{f.FullName.Substring(trimLength)}");
    }
    Console.WriteLine();
}

Bu programın çıkışı, yerel dosya sisteminin ayrıntılarına ve ne olarak ayarlandığına startFolder bağlı olarak uzun olabilir. Tüm sonuçların görüntülenmesini etkinleştirmek için bu örnekte sonuçların nasıl sayfalara bağlanacakları gösterilmektedir. Her grup ayrı numaralandırıldığından iç içe foreach geçmiş bir döngü gereklidir.

Bir klasör kümesindeki toplam bayt sayısını sorgulama

Bu örnekte, belirtilen bir klasördeki tüm dosyalar ve tüm alt klasörleri tarafından kullanılan toplam bayt sayısının nasıl alındığı gösterilmektedir. yöntemi, Sum yan tümcesinde select seçilen tüm öğelerin değerlerini ekler. Bu sorguyu, yerine Sumveya yöntemini çağırarak Min belirtilen dizin ağacındaki en büyük veya Max en küçük dosyayı alacak şekilde değiştirebilirsiniz.

string startFolder = """C:\Program Files\dotnet\sdk""";
// Or
// string startFolder = "/usr/local/share/dotnet/sdk";

var fileList = Directory.GetFiles(startFolder, "*.*", SearchOption.AllDirectories);

var fileQuery = from file in fileList
                let fileLen = new FileInfo(file).Length
                where fileLen > 0
                select fileLen;

// Cache the results to avoid multiple trips to the file system.
long[] fileLengths = fileQuery.ToArray();

// Return the size of the largest file
long largestFile = fileLengths.Max();

// Return the total number of bytes in all the files under the specified folder.
long totalBytes = fileLengths.Sum();

Console.WriteLine($"There are {totalBytes} bytes in {fileList.Count()} files under {startFolder}");
Console.WriteLine($"The largest file is {largestFile} bytes.");

Bu örnek, önceki örneği aşağıdakileri yapacak şekilde genişletir:

  • En büyük dosyanın bayt cinsinden boyutunu alma.
  • En küçük dosyanın bayt cinsinden boyutunu alma.
  • Belirtilen kök klasörün altındaki bir veya daha fazla klasörden en büyük veya en küçük nesne dosyasını alma FileInfo .
  • En büyük 10 dosya gibi bir sırayı alma.
  • Belirtilen boyuttan küçük dosyaları yoksayarak dosyaların bayt cinsinden dosya boyutuna göre gruplar halinde sıralanması.

Aşağıdaki örnek, bayt cinsinden dosya boyutlarına bağlı olarak dosyaları sorgulamayı ve gruplandırma işlemini gösteren beş ayrı sorgu içerir. Sorguyu nesnenin başka bir özelliğine FileInfo dayandırmak için bu örnekleri değiştirebilirsiniz.

// Return the FileInfo object for the largest file
// by sorting and selecting from beginning of list
FileInfo longestFile = (from file in fileList
                        let fileInfo = new FileInfo(file)
                        where fileInfo.Length > 0
                        orderby fileInfo.Length descending
                        select fileInfo
                        ).First();

Console.WriteLine($"The largest file under {startFolder} is {longestFile.FullName} with a length of {longestFile.Length} bytes");

//Return the FileInfo of the smallest file
FileInfo smallestFile = (from file in fileList
                         let fileInfo = new FileInfo(file)
                         where fileInfo.Length > 0
                         orderby fileInfo.Length ascending
                         select fileInfo
                        ).First();

Console.WriteLine($"The smallest file under {startFolder} is {smallestFile.FullName} with a length of {smallestFile.Length} bytes");

//Return the FileInfos for the 10 largest files
var queryTenLargest = (from file in fileList
                       let fileInfo = new FileInfo(file)
                       let len = fileInfo.Length
                       orderby len descending
                       select fileInfo
                      ).Take(10);

Console.WriteLine($"The 10 largest files under {startFolder} are:");

foreach (var v in queryTenLargest)
{
    Console.WriteLine($"{v.FullName}: {v.Length} bytes");
}

// Group the files according to their size, leaving out
// files that are less than 200000 bytes.
var querySizeGroups = from file in fileList
                      let fileInfo = new FileInfo(file)
                      let len = fileInfo.Length
                      where len > 0
                      group fileInfo by (len / 100000) into fileGroup
                      where fileGroup.Key >= 2
                      orderby fileGroup.Key descending
                      select fileGroup;

foreach (var filegroup in querySizeGroups)
{
    Console.WriteLine($"{filegroup.Key}00000");
    foreach (var item in filegroup)
    {
        Console.WriteLine($"\t{item.Name}: {item.Length}");
    }
}

Bir veya daha fazla tam FileInfo nesne döndürmek için sorgunun önce veri kaynağındaki her birini incelemesi ve ardından bunları Length özelliğinin değerine göre sıralaması gerekir. Ardından tek bir veya en büyük uzunluklara sahip diziyi döndürebilir. Listedeki ilk öğeyi döndürmek için kullanın First . İlk n öğe sayısını döndürmek için kullanın Take . Listenin başlangıcına en küçük öğeleri yerleştirmek için azalan bir sıralama düzeni belirtin.

Dizin ağacında yinelenen dosyaları sorgulama

Bazen aynı ada sahip dosyalar birden fazla klasörde bulunabilir. Bu örnekte, belirtilen kök klasör altında bu tür yinelenen dosya adlarının nasıl sorgu yapılacağı gösterilmektedir. İkinci örnekte boyutu ve LastWrite süreleri de eşleşen dosyalar için sorgulama gösterilmektedir.

string startFolder = """C:\Program Files\dotnet\sdk""";
// Or
// string startFolder = "/usr/local/share/dotnet/sdk";

DirectoryInfo dir = new DirectoryInfo(startFolder);

IEnumerable<FileInfo> fileList = dir.GetFiles("*.*", SearchOption.AllDirectories);

// used in WriteLine to keep the lines shorter
int charsToSkip = startFolder.Length;

// var can be used for convenience with groups.
var queryDupNames = from file in fileList
                    group file.FullName.Substring(charsToSkip) by file.Name into fileGroup
                    where fileGroup.Count() > 1
                    select fileGroup;

foreach (var queryDup in queryDupNames.Take(20))
{
    Console.WriteLine($"Filename = {(queryDup.Key.ToString() == string.Empty ? "[none]" : queryDup.Key.ToString())}");

    foreach (var fileName in queryDup.Take(10))
    {
        Console.WriteLine($"\t{fileName}");
    }   
}

İlk sorgu, eşleşmeyi belirlemek için bir anahtar kullanır. Aynı ada sahip ancak içeriği farklı olabilecek dosyaları bulur. İkinci sorgu, nesnenin üç özelliğiyle FileInfo eşleştirmek için bir bileşik anahtar kullanır. Bu sorgunun aynı ada ve benzer veya aynı içeriğe sahip dosyaları bulma olasılığı çok daha yüksektir.

    string startFolder = """C:\Program Files\dotnet\sdk""";
    // Or
    // string startFolder = "/usr/local/share/dotnet/sdk";

    // Make the lines shorter for the console display
    int charsToSkip = startFolder.Length;

    // Take a snapshot of the file system.
    DirectoryInfo dir = new DirectoryInfo(startFolder);
    IEnumerable<FileInfo> fileList = dir.GetFiles("*.*", SearchOption.AllDirectories);

    // Note the use of a compound key. Files that match
    // all three properties belong to the same group.
    // A named type is used to enable the query to be
    // passed to another method. Anonymous types can also be used
    // for composite keys but cannot be passed across method boundaries
    //
    var queryDupFiles = from file in fileList
                        group file.FullName.Substring(charsToSkip) by
                        (Name: file.Name, LastWriteTime: file.LastWriteTime, Length: file.Length )
                        into fileGroup
                        where fileGroup.Count() > 1
                        select fileGroup;

    foreach (var queryDup in queryDupFiles.Take(20))
    {
        Console.WriteLine($"Filename = {(queryDup.Key.ToString() == string.Empty ? "[none]" : queryDup.Key.ToString())}");

        foreach (var fileName in queryDup)
        {
            Console.WriteLine($"\t{fileName}");
        }
    }
}

Bir klasördeki metin dosyalarının içeriğini sorgulama

Bu örnekte, belirtilen dizin ağacındaki tüm dosyaları sorgulama, her dosyayı açma ve içeriğini inceleme gösterilmektedir. Bu tür bir teknik, bir dizin ağacının içeriğinin dizinlerini veya ters dizinlerini oluşturmak için kullanılabilir. Bu örnekte basit bir dize araması gerçekleştirilir. Ancak, normal bir ifadeyle daha karmaşık desen eşleştirme türleri gerçekleştirilebilir.

string startFolder = """C:\Program Files\dotnet\sdk""";
// Or
// string startFolder = "/usr/local/share/dotnet/sdk";

DirectoryInfo dir = new DirectoryInfo(startFolder);

var fileList = dir.GetFiles("*.*", SearchOption.AllDirectories);

string searchTerm = "change";

var queryMatchingFiles = from file in fileList
                         where file.Extension == ".txt"
                         let fileText = File.ReadAllText(file.FullName)
                         where fileText.Contains(searchTerm)
                         select file.FullName;

// Execute the query.
Console.WriteLine($"""The term "{searchTerm}" was found in:""");
foreach (string filename in queryMatchingFiles)
{
    Console.WriteLine(filename);
}

İki klasörün içeriğini karşılaştırma

Bu örnekte iki dosya listesini karşılaştırmanın üç yolu gösterilmektedir:

  • İki dosya listesinin aynı olup olmadığını belirten bir Boole değeri sorgulayarak.
  • Her iki klasördeki dosyaları almak için kesişimi sorgulayarak.
  • Küme farkını sorgulayarak bir klasörde bulunan ancak diğer klasördeki dosyaları almayacak şekilde ayarlayın.

Burada gösterilen teknikler, herhangi bir türdeki nesne dizilerini karşılaştırmak için uyarlanabilir.

Burada gösterilen sınıf, FileComparer Standart Sorgu İşleçleri ile birlikte özel bir karşılaştırıcı sınıfının nasıl kullanılacağını gösterir. sınıfı gerçek dünya senaryolarında kullanılmak üzere tasarlanmamıştır. Her klasörün içeriğinin özdeş olup olmadığını belirlemek için her dosyanın bayt cinsinden adını ve uzunluğunu kullanır. Gerçek dünya senaryosunda, daha sıkı bir eşitlik denetimi gerçekleştirmek için bu karşılaştırıcıyı değiştirmeniz gerekir.

// 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 : IEqualityComparer<FileInfo>
{
    public bool Equals(FileInfo? f1, 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(FileInfo fi)
    {
        string s = $"{fi.Name}{fi.Length}";
        return s.GetHashCode();
    }
}

public static void CompareDirectories()
{
    string pathA = """C:\Program Files\dotnet\sdk\8.0.104""";
    string pathB = """C:\Program Files\dotnet\sdk\8.0.204""";

    DirectoryInfo dir1 = new DirectoryInfo(pathA);
    DirectoryInfo dir2 = new DirectoryInfo(pathB);

    IEnumerable<FileInfo> list1 = dir1.GetFiles("*.*", SearchOption.AllDirectories);
    IEnumerable<FileInfo> list2 = dir2.GetFiles("*.*", SearchOption.AllDirectories);

    //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.Any())
    {
        Console.WriteLine($"The following files are in both folders (total number = {queryCommonFiles.Count()}):");
        foreach (var v in queryCommonFiles.Take(10))
        {
            Console.WriteLine(v.Name); //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.
    var queryList1Only = (from file in list1
                          select file)
                          .Except(list2, myFileCompare);

    Console.WriteLine();
    Console.WriteLine($"The following files are in list1 but not list2 (total number = {queryList1Only.Count()}):");
    foreach (var v in queryList1Only.Take(10))
    {
        Console.WriteLine(v.FullName);
    }

    var queryList2Only = (from file in list2
                          select file)
                          .Except(list1, myFileCompare);

    Console.WriteLine();
    Console.WriteLine($"The following files are in list2 but not list1 (total number = {queryList2Only.Count()}:");
    foreach (var v in queryList2Only.Take(10))
    {
        Console.WriteLine(v.FullName);
    }
}

Sınırlandırılmış bir dosyanın alanlarını yeniden sıralama

Virgülle ayrılmış değer (CSV) dosyası, genellikle elektronik tablo verilerini veya satırlar ve sütunlar tarafından temsil edilen diğer tablosal verileri depolamak için kullanılan bir metin dosyasıdır. Alanları ayırmak için yöntemini kullanarak Split LINQ kullanarak CSV dosyalarını sorgulamak ve işlemek kolaydır. Aslında, aynı teknik herhangi bir yapılandırılmış metin satırının bölümlerini yeniden sıralamak için kullanılabilir; CSV dosyalarıyla sınırlı değildir.

Aşağıdaki örnekte, üç sütunun öğrencilerin "aile adı", "ad" ve "Kimlik" sütunlarını temsil ettiğini varsayalım. Alanlar, öğrencilerin aile adlarına göre alfabetik sıradadır. Sorgu, kimlik sütununun ilk sırada göründüğü yeni bir sıra oluşturur ve ardından öğrencinin adıyla aile adını birleştiren ikinci bir sütun oluşturur. Satırlar, kimlik alanına göre yeniden sıralanır. Sonuçlar yeni bir dosyaya kaydedilir ve özgün veriler değiştirilmez. Aşağıdaki metin, aşağıdaki örnekte kullanılan spreadsheet1.csv dosyasının içeriğini gösterir:

Adams,Terry,120
Fakhouri,Fadi,116
Feng,Hanying,117
Garcia,Cesar,114
Garcia,Debra,115
Garcia,Hugo,118
Mortensen,Sven,113
O'Donnell,Claire,112
Omelchenko,Svetlana,111
Tucker,Lance,119
Tucker,Michael,122
Zabokritski,Eugene,121

Aşağıdaki kod kaynak dosyayı okur ve CSV dosyasındaki her sütunu yeniden düzenleyerek sütunların sırasını yeniden düzenler:

string[] lines = File.ReadAllLines("spreadsheet1.csv");

// Create the query. Put field 2 first, then
// reverse and combine fields 0 and 1 from the old field
IEnumerable<string> query = from line in lines
                            let fields = line.Split(',')
                            orderby fields[2]
                            select $"{fields[2]}, {fields[1]} {fields[0]}";

File.WriteAllLines("spreadsheet2.csv", query.ToArray());

/* Output to spreadsheet2.csv:
111, Svetlana Omelchenko
112, Claire O'Donnell
113, Sven Mortensen
114, Cesar Garcia
115, Debra Garcia
116, Fadi Fakhouri
117, Hanying Feng
118, Hugo Garcia
119, Lance Tucker
120, Terry Adams
121, Eugene Zabokritski
122, Michael Tucker
*/

Grupları kullanarak bir dosyayı birçok dosyaya bölme

Bu örnek, iki dosyanın içeriğini birleştirmenin ve ardından verileri yeni bir şekilde düzenleyen bir dizi yeni dosya oluşturmanın bir yolunu gösterir. Sorgu iki dosyanın içeriğini kullanır. Aşağıdaki metin, names1.txt ilk dosyanın içeriğini gösterir:

Bankov, Peter
Holm, Michael
Garcia, Hugo
Potra, Cristina
Noriega, Fabricio
Aw, Kam Foo
Beebe, Ann
Toyoshima, Tim
Guy, Wey Yuan
Garcia, Debra

İkinci dosya olan names2.txt, bazıları ilk kümeyle ortak olan farklı bir ad kümesi içerir:

Liu, Jinghao
Bankov, Peter
Holm, Michael
Garcia, Hugo
Beebe, Ann
Gilchrist, Beth
Myrcha, Jacek
Giakoumakis, Leo
McLin, Nkenge
El Yassir, Mehdi

Aşağıdaki kod her iki dosyayı da sorgular, her iki dosyanın birleşimini alır, ardından her grup için aile adının ilk harfiyle tanımlanan yeni bir dosya yazar:

string[] fileA = File.ReadAllLines("names1.txt");
string[] fileB = File.ReadAllLines("names2.txt");

// Concatenate and remove duplicate names
var mergeQuery = fileA.Union(fileB);

// Group the names by the first letter in the last name.
var groupQuery = from name in mergeQuery
                 let n = name.Split(',')[0]
                 group name by n[0] into g
                 orderby g.Key
                 select g;

foreach (var g in groupQuery)
{
    string fileName = $"testFile_{g.Key}.txt";

    Console.WriteLine(g.Key);

    using StreamWriter sw = new StreamWriter(fileName);
    foreach (var item in g)
    {
        sw.WriteLine(item);
        // Output to console for example purposes.
        Console.WriteLine($"   {item}");
    }
}
/* Output:
    A
       Aw, Kam Foo
    B
       Bankov, Peter
       Beebe, Ann
    E
       El Yassir, Mehdi
    G
       Garcia, Hugo
       Guy, Wey Yuan
       Garcia, Debra
       Gilchrist, Beth
       Giakoumakis, Leo
    H
       Holm, Michael
    L
       Liu, Jinghao
    M
       Myrcha, Jacek
       McLin, Nkenge
    N
       Noriega, Fabricio
    P
       Potra, Cristina
    T
       Toyoshima, Tim
 */

Farklı dosyalardan içerik birleştirme

Bu örnek, eşleşen anahtar olarak kullanılan ortak bir değeri paylaşan virgülle ayrılmış iki dosyadan verilerin nasıl birleştirildiğini gösterir. Bu teknik, iki elektronik tablodan veya elektronik tablodan ve başka biçime sahip bir dosyadan verileri yeni bir dosyada birleştirmeniz gerekiyorsa yararlı olabilir. Örneği, herhangi bir tür yapılandırılmış metinle çalışacak şekilde değiştirebilirsiniz.

Aşağıdaki metinde scores.csv içeriği gösterilmektedir. Dosya elektronik tablo verilerini temsil eder. Sütun 1 öğrencinin kimliğidir ve 2 ile 5 arasında sütunlar test puanlarıdır.

111, 97, 92, 81, 60
112, 75, 84, 91, 39
113, 88, 94, 65, 91
114, 97, 89, 85, 82
115, 35, 72, 91, 70
116, 99, 86, 90, 94
117, 93, 92, 80, 87
118, 92, 90, 83, 78
119, 68, 79, 88, 92
120, 99, 82, 81, 79
121, 96, 85, 91, 60
122, 94, 92, 91, 91

Aşağıdaki metinde names.csv içeriği gösterilmektedir. Dosya, öğrencinin aile adını, adını ve öğrenci kimliğini içeren bir elektronik tabloyu temsil eder.

Omelchenko,Svetlana,111
O'Donnell,Claire,112
Mortensen,Sven,113
Garcia,Cesar,114
Garcia,Debra,115
Fakhouri,Fadi,116
Feng,Hanying,117
Garcia,Hugo,118
Tucker,Lance,119
Adams,Terry,120
Zabokritski,Eugene,121
Tucker,Michael,122

İlgili bilgileri içeren benzer olmayan dosyalardan içeriği birleştirin. Dosya names.csv öğrenci adını ve kimlik numarasını içerir. Dosya scores.csv kimliği ve dört test puanı kümesini içerir. Aşağıdaki sorgu, eşleşen anahtar olarak kimlik kullanarak puanları öğrenci adlarına ekler. Kod aşağıdaki örnekte gösterilmiştir:

string[] names = File.ReadAllLines(@"names.csv");
string[] scores = File.ReadAllLines(@"scores.csv");

var scoreQuery = from name in names
                  let nameFields = name.Split(',')
                  from id in scores
                  let scoreFields = id.Split(',')
                  where Convert.ToInt32(nameFields[2]) == Convert.ToInt32(scoreFields[0])
                  select $"{nameFields[0]},{scoreFields[1]},{scoreFields[2]},{scoreFields[3]},{scoreFields[4]}";

Console.WriteLine("\r\nMerge two spreadsheets:");
foreach (string item in scoreQuery)
{
    Console.WriteLine(item);
}
Console.WriteLine("{0} total names in list", scoreQuery.Count());
/* Output:
Merge two spreadsheets:
Omelchenko, 97, 92, 81, 60
O'Donnell, 75, 84, 91, 39
Mortensen, 88, 94, 65, 91
Garcia, 97, 89, 85, 82
Garcia, 35, 72, 91, 70
Fakhouri, 99, 86, 90, 94
Feng, 93, 92, 80, 87
Garcia, 92, 90, 83, 78
Tucker, 68, 79, 88, 92
Adams, 99, 82, 81, 79
Zabokritski, 96, 85, 91, 60
Tucker, 94, 92, 91, 91
12 total names in list
 */

CSV metin dosyasındaki sütun değerlerini hesaplama

Bu örnekte, .csv dosyasının sütunlarında Sum, Average, Min ve Max gibi toplam hesaplamaların nasıl gerçekleştirildiği gösterilmektedir. Burada gösterilen örnek ilkeler diğer yapılandırılmış metin türlerine uygulanabilir.

Aşağıdaki metinde scores.csv içeriği gösterilmektedir. İlk sütunun bir öğrenci kimliğini, izleyen sütunların ise dört sınavın puanlarını temsil ettiğini varsayalım.

111, 97, 92, 81, 60
112, 75, 84, 91, 39
113, 88, 94, 65, 91
114, 97, 89, 85, 82
115, 35, 72, 91, 70
116, 99, 86, 90, 94
117, 93, 92, 80, 87
118, 92, 90, 83, 78
119, 68, 79, 88, 92
120, 99, 82, 81, 79
121, 96, 85, 91, 60
122, 94, 92, 91, 91

Aşağıdaki metinde, her metin satırını bir diziye dönüştürmek için yönteminin nasıl kullanılacağı Split gösterilmektedir. Her dizi öğesi bir sütunu temsil eder. Son olarak, her sütundaki metin kendi sayısal gösterimine dönüştürülür.

public class SumColumns
{
    public static void SumCSVColumns(string fileName)
    {
        string[] lines = File.ReadAllLines(fileName);

        // Specifies the column to compute.
        int exam = 3;

        // Spreadsheet format:
        // Student ID    Exam#1  Exam#2  Exam#3  Exam#4
        // 111,          97,     92,     81,     60

        // Add one to exam to skip over the first column,
        // which holds the student ID.
        SingleColumn(lines, exam + 1);
        Console.WriteLine();
        MultiColumns(lines);
    }

    static void SingleColumn(IEnumerable<string> strs, int examNum)
    {
        Console.WriteLine("Single Column Query:");

        // Parameter examNum specifies the column to
        // run the calculations on. This value could be
        // passed in dynamically at run time.

        // Variable columnQuery is an IEnumerable<int>.
        // The following query performs two steps:
        // 1) use Split to break each row (a string) into an array
        //    of strings,
        // 2) convert the element at position examNum to an int
        //    and select it.
        var columnQuery = from line in strs
                          let elements = line.Split(',')
                          select Convert.ToInt32(elements[examNum]);

        // Execute the query and cache the results to improve
        // performance. This is helpful only with very large files.
        var results = columnQuery.ToList();

        // Perform aggregate calculations Average, Max, and
        // Min on the column specified by examNum.
        double average = results.Average();
        int max = results.Max();
        int min = results.Min();

        Console.WriteLine($"Exam #{examNum}: Average:{average:##.##} High Score:{max} Low Score:{min}");
    }

    static void MultiColumns(IEnumerable<string> strs)
    {
        Console.WriteLine("Multi Column Query:");

        // Create a query, multiColQuery. Explicit typing is used
        // to make clear that, when executed, multiColQuery produces
        // nested sequences. However, you get the same results by
        // using 'var'.

        // The multiColQuery query performs the following steps:
        // 1) use Split to break each row (a string) into an array
        //    of strings,
        // 2) use Skip to skip the "Student ID" column, and store the
        //    rest of the row in scores.
        // 3) convert each score in the current row from a string to
        //    an int, and select that entire sequence as one row
        //    in the results.
        var multiColQuery = from line in strs
                            let elements = line.Split(',')
                            let scores = elements.Skip(1)
                            select (from str in scores
                                    select Convert.ToInt32(str));

        // Execute the query and cache the results to improve
        // performance.
        // ToArray could be used instead of ToList.
        var results = multiColQuery.ToList();

        // Find out how many columns you have in results.
        int columnCount = results[0].Count();

        // Perform aggregate calculations Average, Max, and
        // Min on each column.
        // Perform one iteration of the loop for each column
        // of scores.
        // You can use a for loop instead of a foreach loop
        // because you already executed the multiColQuery
        // query by calling ToList.
        for (int column = 0; column < columnCount; column++)
        {
            var results2 = from row in results
                           select row.ElementAt(column);
            double average = results2.Average();
            int max = results2.Max();
            int min = results2.Min();

            // Add one to column because the first exam is Exam #1,
            // not Exam #0.
            Console.WriteLine($"Exam #{column + 1} Average: {average:##.##} High Score: {max} Low Score: {min}");
        }
    }
}
/* Output:
    Single Column Query:
    Exam #4: Average:76.92 High Score:94 Low Score:39

    Multi Column Query:
    Exam #1 Average: 86.08 High Score: 99 Low Score: 35
    Exam #2 Average: 86.42 High Score: 94 Low Score: 72
    Exam #3 Average: 84.75 High Score: 91 Low Score: 65
    Exam #4 Average: 76.92 High Score: 94 Low Score: 39
 */

Dosyanız sekmeyle ayrılmış bir dosyaysa, yöntemindeki Split bağımsız değişkeni olarak güncelleştirmeniz yeter \t.