Cara: Menggunakan LINQ untuk mengkueri file dan direktori

Banyak operasi sistem file pada dasarnya adalah kueri dan oleh karena itu sangat cocok untuk pendekatan LINQ. Kueri ini tidak merusak. Mereka tidak mengubah konten file atau folder asli. Kueri tidak boleh menyebabkan efek samping apa pun. Secara umum, kode apa pun (termasuk kueri yang melakukan operasi buat/perbarui/hapus) yang memodifikasi data sumber harus dipisahkan dari kode yang hanya mengkueri data.

Ada beberapa kompleksitas yang terlibat dalam membuat sumber data yang secara akurat mewakili konten sistem file dan menangani pengecualian dengan anggun. Contoh dalam bagian ini membuat kumpulan snapshot objek FileInfo yang mewakili semua file di bawah folder akar tertentu dan semua subfoldernya. Status aktual masing-masing FileInfo mungkin berubah dalam waktu antara saat Anda memulai dan mengakhiri eksekusi kueri. Misalnya, Anda dapat membuat daftar objek FileInfo untuk digunakan sebagai sumber data. Jika Anda mencoba mengakses Length properti dalam kueri, FileInfo objek mencoba mengakses sistem file untuk memperbarui nilai Length. Jika file tidak lagi ada, Anda mendapatkan FileNotFoundException dalam kueri Anda, meskipun Anda tidak mengkueri sistem file secara langsung.

Cara mengkueri file dengan atribut atau nama tertentu

Contoh ini menunjukkan cara menemukan semua file yang memiliki ekstensi nama file tertentu (misalnya ".txt") di pohon direktori tertentu. Ini juga menunjukkan cara mengembalikan file terbaru atau terlama di pohon berdasarkan waktu pembuatan. Anda mungkin perlu memodifikasi baris pertama dari banyak sampel apakah Anda menjalankan kode ini di sistem Windows, Mac, atau Linux.

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}");

Cara mengelompokkan file menurut ekstensi

Contoh ini menunjukkan cara LINQ digunakan untuk melakukan operasi pengelompokan dan pengurutan lanjutan pada daftar file atau folder. Contoh ini juga menunjukkan cara membagi keluaran ke dalam jendela konsol dengan menggunakan metode Skip dan Take.

Kueri berikut ini menunjukkan cara mengelompokkan konten pohon direktori tertentu berdasarkan ekstensi nama file.

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();
}

Output dari program ini dapat berukuran panjang, tergantung pada detail sistem file lokal dan pengaturan startFolder. Untuk mengaktifkan tampilan semua hasil, contoh ini menunjukkan cara membagi melalui hasil. Perulangan berlapis foreach diperlukan karena setiap grup dijumlahkan secara terpisah.

Cara mengkueri jumlah total byte dalam sekumpulan folder

Contoh ini menunjukkan cara mengambil jumlah total byte yang digunakan oleh semua file dalam folder tertentu dan semua subfoldernya. Metode Sum menambahkan nilai semua item yang dipilih dalam klausul select. Anda dapat mengubah kueri ini untuk mengambil file terbesar atau terkecil di pohon direktori yang ditentukan dengan memanggil Min metode atau Max alih-alih Sum.

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.");

Contoh ini memperluas contoh sebelumnya untuk melakukan hal berikut:

  • Cara mengambil ukuran dalam byte dari file terbesar.
  • Cara mengambil ukuran dalam byte dari file terkecil.
  • Cara mengambil file terbesar atau terkecil objek FileInfo dari satu atau lebih folder pada bagian folder root yang ditentukan.
  • Cara mengambil urutan seperti 10 file terbesar.
  • Cara mengurutkan file ke dalam grup berdasarkan ukuran file dalam byte, mengabaikan file yang kurang dari ukuran yang ditentukan.

Contoh berikut berisi lima kueri terpisah yang memperlihatkan cara kueri dan mengelompokkan file, bergantung pada ukuran filenya dalam byte. Anda dapat mengubah contoh-contoh ini untuk mendasarkan kueri pada beberapa properti FileInfo objek lainnya.

// 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}");
    }
}

Untuk mengembalikan satu atau beberapa objek FileInfo lengkap, kueri terlebih dahulu harus memeriksa masing-masing objek di sumber data, lalu mengurutkannya menurut nilai properti Panjangnya. Kemudian dapat mengembalikan satu atau urutan dengan panjang terbesar. Gunakan First untuk mengembalikan elemen pertama dalam daftar. Gunakan Take untuk mengembalikan n jumlah elemen pertama. Tentukan urutan pengurutan menurun untuk menempatkan elemen terkecil di awal daftar.

Cara mengkueri file duplikat di pohon direktori

Terkadang file yang memiliki nama yang sama dapat ditemukan di lebih dari satu folder. Contoh ini menunjukkan cara mengkueri untuk nama file duplikat tersebut di bawah folder akar yang ditentukan. Contoh kedua menunjukkan cara mengkueri untuk file yang ukurannya dan waktu LastWrite juga cocok.

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}");
    }   
}

Kueri pertama menggunakan kunci untuk menentukan kecocokan. Ini menemukan file yang memiliki nama yang sama tetapi kontennya mungkin berbeda. Kueri kedua menggunakan kunci campuran untuk dicocokkan dengan tiga properti objek FileInfo. Kueri ini jauh lebih mungkin untuk menemukan file yang memiliki nama yang sama dan isi yang serupa atau identik.

    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}");
        }
    }
}

Cara mengkueri konten file teks dalam folder

Contoh ini memperlihatkan cara mengkueri semua file dalam pohon direktori tertentu, membuka setiap file, dan memeriksa kontennya. Jenis teknik ini dapat digunakan untuk membuat indeks atau membalikkan indeks dari isi pohon direktori. Pencarian string yang sederhana dilakukan dalam contoh ini. Namun, jenis pencocokan pola yang lebih kompleks dapat dilakukan dengan ekspresi reguler.

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);
}

Cara membandingkan konten dua folder

Contoh ini menunjukkan tiga cara untuk membandingkan dua daftar file:

  • Dengan mengkueri nilai Boolean yang menentukan jika dua daftar file identik.
  • Dengan mengkueri persimpangan untuk mengambil file yang ada di kedua folder.
  • Dengan mengkueri perbedaan yang ditetapkan untuk mengambil file yang berada dalam satu folder, tetapi tidak di yang lainnya.

Teknik yang ditunjukkan di sini dapat disesuaikan untuk membandingkan urutan objek dari jenis apa pun.

Kelas FileComparer yang diperlihatkan di sini menunjukkan cara menggunakan kelas perbandingan kustom bersama Operator Kueri Standar. Kelas ini tidak dimaksudkan untuk digunakan dalam skenario dunia nyata. Itu hanya menggunakan nama dan panjang dalam byte dari setiap file untuk menentukan apakah isi setiap folder identik atau tidak. Dalam skenario dunia nyata, Anda harus memodifikasi pembanding ini untuk melakukan pemeriksaan kesetaraan yang lebih ketat.

// 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);
    }
}

Cara menyusun ulang bidang file yang dibatasi

File nilai yang dipisahkan koma (CSV) adalah file teks yang sering digunakan untuk menyimpan data spreadsheet atau data tabular lainnya yang diwakili oleh baris dan kolom. Dengan menggunakan Split metode untuk memisahkan bidang, mudah untuk mengkueri dan memanipulasi file CSV menggunakan LINQ. Bahkan, teknik yang sama dapat digunakan untuk menyusun ulang bagian dari baris teks terstruktur apa pun; tidak terbatas pada file CSV.

Dalam contoh berikut, asumsikan bahwa tiga kolom mewakili "nama keluarga," "nama depan" siswa, dan "ID." Bidang dalam urutan alfabet berdasarkan nama keluarga siswa. Kueri menghasilkan urutan baru di mana kolom ID muncul terlebih dahulu, diikuti dengan kolom kedua yang menggabungkan nama depan siswa dan nama keluarga. Baris disusun ulang sesuai dengan bidang ID. Hasilnya disimpan ke dalam file baru dan data asli tidak dimodifikasi. Teks berikut menunjukkan konten file spreadsheet1.csv yang digunakan dalam contoh berikut:

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

Kode berikut membaca file sumber dan menyusun ulang setiap kolom dalam file CSV untuk menyusun ulang urutan kolom:

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
*/

Cara membagi file menjadi banyak file dengan menggunakan grup

Contoh ini menunjukkan salah satu cara untuk menggabungkan konten dua file dan kemudian membuat satu set file baru yang mengatur data dengan cara baru. Kueri menggunakan konten dua file. Teks berikut menunjukkan konten file pertama, names1.txt:

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

File kedua, names2.txt, berisi sekumpulan nama yang berbeda, beberapa di antaranya sama dengan set pertama:

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

Kode berikut mengkueri kedua file, mengambil penyatuan kedua file, lalu menulis file baru untuk setiap grup, yang ditentukan oleh huruf pertama nama keluarga:

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
 */

Cara menggabungkan konten dari file yang berbeda

Contoh ini menunjukkan cara menggabungkan data dari dua file yang dipisahkan koma yang berbagi nilai umum yang digunakan sebagai kunci yang cocok. Teknik ini berguna jika Anda harus menggabungkan data dari dua spreadsheet, atau dari spreadsheet dan dari file yang memiliki format lain, ke dalam file baru. Anda dapat memodifikasi contoh untuk bekerja dengan semua jenis teks terstruktur.

Teks berikut menunjukkan konten scores.csv. File merepresentasikan data spreadsheet. Kolom 1 adalah ID siswa, dan kolom 2 hingga 5 adalah skor tes.

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

Teks berikut menunjukkan konten names.csv. File mewakili spreadsheet yang berisi nama keluarga siswa, nama depan, dan ID siswa.

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

Gabungkan konten dari file berbeda yang berisi informasi terkait. File names.csv berisi nama siswa ditambah nomor ID. File scores.csv berisi ID dan satu set empat skor pengujian. Kueri berikut menggabungkan skor ke nama siswa dengan menggunakan ID sebagai kunci yang cocok. Kode ditampilkan dalam contoh berikut:

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
 */

Cara menghitung nilai kolom dalam file teks CSV

Contoh ini menunjukkan cara melakukan penghitungan agregat seperti Sum, Average, Min, dan Max pada kolom file .csv. Prinsip contoh yang ditampilkan di sini dapat diterapkan pada jenis teks terstruktur lainnya.

Teks berikut menunjukkan konten scores.csv. Asumsikan bahwa kolom pertama mewakili ID siswa, dan kolom berikutnya mewakili skor dari empat ujian.

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

Teks berikut menunjukkan cara menggunakan Split metode untuk mengonversi setiap baris teks menjadi array. Setiap elemen larik mewakili kolom. Akhirnya, teks di setiap kolom diubah menjadi representasi numerik teks.

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
 */

Jika file Anda adalah file yang dipisahkan tab, cukup perbarui argumen dalam metode Split ke \t.