Megosztás a következőn keresztül:


Útmutató: Fájlok és könyvtárak lekérdezése a LINQ használatával

Számos fájlrendszerművelet alapvetően lekérdezés, ezért jól illeszkedik a LINQ megközelítéshez. Ezek a lekérdezések nem strukturáltak. Nem módosítják az eredeti fájlok vagy mappák tartalmát. A lekérdezések nem okozhatnak mellékhatásokat. Általánosságban elmondható, hogy a forrásadatokat módosító kódokat (beleértve a létrehozási/frissítési/törlési műveleteket) külön kell tartani az adatokat lekérdező kódtól.

Van némi összetettség egy olyan adatforrás létrehozása során, amely pontosan képviseli a fájlrendszer tartalmát, és a kivételeket elegánsan kezeli. Az ebben a szakaszban szereplő példák olyan objektumok pillanatképgyűjteményét FileInfo hozzák létre, amelyek egy adott gyökérmappában lévő összes fájlt és annak almappáit jelölik. Az egyes FileInfo elemek tényleges állapota megváltozhat a lekérdezések végrehajtásának megkezdése és befejezése közötti időszakban. Létrehozhat például adatforrásként használandó objektumok listáját FileInfo . Ha egy lekérdezésben megpróbál hozzáférni a Length tulajdonsághoz, az FileInfo objektum megpróbál hozzáférni a fájlrendszerhez, hogy frissítse a tulajdonság értékét Length. Ha a fájl már nem létezik, megjelenik egy FileNotFoundException lekérdezés, még akkor is, ha nem kérdezi le közvetlenül a fájlrendszert.

Adott attribútummal vagy névvel rendelkező fájlok lekérdezése

Ez a példa bemutatja, hogyan kereshet meg minden olyan fájlt, amely rendelkezik egy megadott fájlnévkiterjesztéssel (például ".txt") egy megadott könyvtárfán. Azt is bemutatja, hogyan adja vissza a fa legújabb vagy legrégebbi fájlját a létrehozási idő alapján. Előfordulhat, hogy módosítania kell a minták első sorát, függetlenül attól, hogy a kódot Windows, Mac vagy Linux rendszeren futtatja.

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

Fájlok csoportosítása bővítmény szerint

Ez a példa bemutatja, hogyan használható a LINQ speciális csoportosítási és rendezési műveletek végrehajtására fájlok vagy mappák listáján. Azt is bemutatja, hogyan jelenítheti meg a kimenetet a konzolablakban a metódusok és Take a Skip metódusok használatával.

Az alábbi lekérdezés bemutatja, hogyan csoportosíthatja egy adott könyvtárfa tartalmát a fájlnévkiterjesztés alapján.

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

A program kimenete a helyi fájlrendszer részleteitől és a startFolder beállított értéktől függően hosszú lehet. Az összes találat megtekintésének engedélyezéséhez ez a példa bemutatja, hogyan jelenítheti meg az eredményeket. Beágyazott foreach hurokra van szükség, mert minden csoport külön van felsorolva.

A mappákban lévő bájtok teljes számának lekérdezése

Ez a példa bemutatja, hogyan kérhető le a megadott mappában lévő összes fájl és annak almappái által használt bájtok teljes száma. A Sum metódus hozzáadja a záradékban select kijelölt összes elem értékét. Ezt a lekérdezést úgy módosíthatja, hogy a megadott könyvtárfa legnagyobb vagy legkisebb fájlját kérje le ahelyett, hogy a metódust vagy Max a Min metódust Sumhívja meg.

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

Ez a példa kiterjeszti az előző példát a következők végrehajtására:

  • A méret lekérése bájtban a legnagyobb fájlban.
  • A méret lekérése bájtban a legkisebb fájlban.
  • Az objektum legnagyobb vagy legkisebb fájljának FileInfo lekérése egy vagy több mappából egy adott gyökérmappában.
  • Hogyan lehet lekérni egy sorozatot, például a 10 legnagyobb fájlt.
  • Fájlok csoportokba rendelése a fájlméretük alapján bájtban, figyelmen kívül hagyva a megadott méretnél kisebb fájlokat.

Az alábbi példa öt külön lekérdezést tartalmaz, amelyek bemutatják a fájlok lekérdezését és csoportosítását a bájtokban lévő fájlméretüktől függően. Ezeket a példákat úgy módosíthatja, hogy a lekérdezés az FileInfo objektum egy másik tulajdonságán alapuljon.

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

Egy vagy több teljes FileInfo objektum visszaadásához a lekérdezésnek először meg kell vizsgálnia mindegyiket az adatforrásban, majd rendeznie kell őket a Hossz tulajdonság értéke alapján. Ezután visszaadhatja az egyetlent vagy a legnagyobb hosszúságú sorozatot. A lista első elemének visszaadására használható First . Az elemek első n számának visszaadására használható Take . Adjon meg csökkenő rendezési sorrendet, hogy a legkisebb elemeket a lista elejére helyezze.

Duplikált fájlok lekérdezése könyvtárfán

Néha az azonos nevű fájlok több mappában is elhelyezhetők. Ez a példa bemutatja, hogyan kérdezhet le ilyen duplikált fájlneveket egy adott gyökérmappában. A második példa bemutatja, hogyan kérdezheti le azokat a fájlokat, amelyeknek a mérete és a LastWrite ideje is megegyezik.

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

Az első lekérdezés kulcs használatával határozza meg az egyezést. Megkeresi azokat a fájlokat, amelyeknek ugyanaz a neve, de a tartalma eltérő lehet. A második lekérdezés összetett kulccsal egyezik az FileInfo objektum három tulajdonságával. Ez a lekérdezés sokkal valószínűbb, hogy azonos nevű és hasonló vagy azonos tartalommal rendelkező fájlokat talál.

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

Szövegfájlok tartalmának lekérdezése egy mappában

Ez a példa bemutatja, hogyan kérdezheti le egy adott könyvtárfa összes fájlját, hogyan nyithatja meg az egyes fájlokat, és hogyan vizsgálhatja meg annak tartalmát. Ez a módszer használható egy könyvtárfa tartalmának indexeinek vagy fordított indexeinek létrehozására. Ebben a példában egyszerű sztringkeresést hajtunk végre. A mintaegyeztetés összetettebb típusai azonban normál kifejezéssel is elvégezhetők.

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

Két mappa tartalmának összehasonlítása

Ez a példa három módszert mutat be két fájlelem összehasonlítására:

  • Egy logikai érték lekérdezésével, amely meghatározza, hogy a két fájllista azonos-e.
  • A metszet lekérdezésével lekérheti a mindkét mappában lévő fájlokat.
  • A beállítás különbségének lekérdezésével lekérheti az egyik mappában lévő fájlokat, a másikat azonban nem.

Az itt bemutatott technikák bármilyen típusú objektumsorozat összehasonlítása érdekében módosíthatók.

Az FileComparer itt látható osztály bemutatja, hogyan használható egyéni összehasonlító osztály a Standard lekérdezési operátorokkal együtt. Az osztály nem valós helyzetekben való használatra készült. Csak az egyes fájlok nevét és hosszát használja annak megállapítására, hogy az egyes mappák tartalma azonos-e vagy sem. Valós forgatókönyv esetén módosítania kell ezt az összehasonlítót, hogy szigorúbb egyenlőség-ellenőrzést végezzen.

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

Tagolt fájl mezőinek átrendezése

A vesszővel tagolt értékfájl (CSV) olyan szövegfájl, amelyet gyakran használnak táblázatos adatok vagy sorok és oszlopok által ábrázolt táblázatos adatok tárolására. A mezők elválasztására szolgáló Split módszerrel könnyen lekérdezhetőek és kezelhetők a CSV-fájlok a LINQ használatával. Valójában ugyanez a technika bármely strukturált szövegsor részeinek átrendezésére használható; ez nem csak CSV-fájlokra korlátozódik.

A következő példában feltételezzük, hogy a három oszlop a diákok "családnevét", "utónevét" és "azonosítóját" jelöli. A mezők betűrendbe vannak rendezve a diákok családnevei alapján. A lekérdezés egy új sorozatot hoz létre, amelyben az azonosító oszlop jelenik meg először, majd egy második oszlopot, amely egyesíti a tanuló utónevét és családnevét. A sorok az Azonosító mező szerint vannak átrendezve. Az eredmények új fájlba kerülnek, és az eredeti adatok nem módosulnak. Az alábbi szöveg az alábbi példában használt spreadsheet1.csv fájl tartalmát mutatja be:

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 következő kód beolvassa a forrásfájlt, és átrendezi a CSV-fájl minden oszlopát az oszlopok sorrendjének átrendezéséhez:

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

Fájl felosztása több fájlra csoportok használatával

Ez a példa azt mutatja be, hogyan egyesítheti két fájl tartalmát, majd létrehozhat egy új fájlkészletet, amely új módon rendszerezi az adatokat. A lekérdezés két fájl tartalmát használja. Az alábbi szöveg az első fájl tartalmát jeleníti meg, names1.txt:

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

A második fájl( names2.txt) eltérő névkészletet tartalmaz, amelyek közül néhány közös az első készlettel:

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

Az alábbi kód mindkét fájlt lekérdezi, a két fájl egyesítését veszi igénybe, majd minden csoporthoz új fájlt ír, amelyet a családnév első betűje határoz meg:

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

Tartalom csatlakoztatása eltérő fájlokból

Ez a példa bemutatja, hogyan illesztheti össze az adatokat két vesszővel tagolt fájlból, amelyek közös értéket használnak egyező kulcsként. Ez a technika akkor lehet hasznos, ha két számolótáblából vagy egy számolótáblából és egy másik formátumú fájlból származó adatokat kell egy új fájlba egyesítenie. A példát úgy módosíthatja, hogy bármilyen strukturált szöveggel működjön.

Az alábbi szöveg a scores.csv tartalmát jeleníti meg. A fájl a számolótábla adatait jelöli. Az 1. oszlop a tanuló azonosítója, a 2–5. oszlop pedig teszteredmény.

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

Az alábbi szöveg a names.csv tartalmát jeleníti meg. A fájl egy számolótáblát jelöl, amely tartalmazza a tanuló családnevét, utónevét és diákazonosítóját.

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

A kapcsolódó információkat tartalmazó eltérő fájlokból származó tartalmak összekapcsolása. A names.csv fájl tartalmazza a tanuló nevét és egy azonosítószámot. A fájl scores.csv tartalmazza az azonosítót és négy tesztpontszámot. Az alábbi lekérdezés a pontszámokat egyező kulcsként használt azonosítóval illeszti a diákok nevéhez. A kód a következő példában látható:

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

Oszlopértékek kiszámítása CSV-szövegfájlban

Ez a példa bemutatja, hogyan hajthat végre összesítő számításokat, például sum, Average, Min és Max a .csv fájl oszlopán. Az itt látható példaelvek más típusú strukturált szövegekre is alkalmazhatók.

Az alábbi szöveg a scores.csv tartalmát jeleníti meg. Tegyük fel, hogy az első oszlop egy tanulóazonosítót, az azt követő oszlopok pedig négy vizsga pontszámait jelölik.

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

Az alábbi szöveg bemutatja, hogyan konvertálhatja az egyes szövegsorokat tömbökké a Split metódus használatával. Minden tömbelem egy oszlopot jelöl. Végül az egyes oszlopok szövegét a program numerikus ábrázolásúvá alakítja.

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

Ha a fájl tabulátorral elválasztott fájl, frissítse a metódus argumentumát a Split következőre \t: .