C#中的LINQ?to?Objects詳解(1)
相關(guān)文章:
一、概述
LINQ to Objects (C#) | Microsoft 官方文檔
“LINQ to Objects” 指直接將 LINQ 查詢與任何 IEnumerable 或 IEnumerable 集合一起使用。
可以使用 LINQ 來查詢?nèi)魏慰擅杜e的集合,例如 List、Array 或Dictionary<TKey,TValue>。 該集合可以是用戶定義的集合,也可以是由 .NET Framework API 返回的集合。
“LINQ to Objects”表示一種新的處理集合的方法。 采用舊方法,必須編寫指定如何從集合檢索數(shù)據(jù)的復(fù)雜的 foreach 循環(huán)。 而采用 LINQ 方法,只需編寫描述要檢索的內(nèi)容的聲明性代碼。
此外,LINQ 查詢與傳統(tǒng) foreach 循環(huán)相比具有三大優(yōu)勢(shì):
- 它們更簡(jiǎn)明、更易讀,尤其在篩選多個(gè)條件時(shí)。
- 它們使用最少的應(yīng)用程序代碼提供強(qiáng)大的篩選、排序和分組功能。
- 無需修改或只需做很小的修改即可將它們移植到其他數(shù)據(jù)源。
二、 Linq to Objects中的延遲計(jì)算
Linq查詢的延遲計(jì)算原理:通過給LINQ擴(kuò)展方法傳遞方法委托,作為yield迭代器的主體,讓遍歷執(zhí)行到MoveNext()時(shí)才執(zhí)行耗時(shí)的指令。
1. Linq延遲計(jì)算的注意點(diǎn)
以下代碼我們?cè)镜钠谕Y(jié)果是:刪除掉字符串中所有的原音字母。但現(xiàn)在只刪除’u’,因?yàn)閕tem變量是循環(huán)外部聲明的,同一個(gè)變量重復(fù)聲明更新,所以當(dāng)最后賦值時(shí),item記錄的是最后一個(gè)值為'u'。在foreach才真正執(zhí)行Where查詢。
IEnumerable<char> query = "Not what you might expect"; var item = 'a'; query = query.Where(c => c != item); item = 'e'; query = query.Where(c => c != item); item = 'i'; query = query.Where(c => c != item); item = 'o'; query = query.Where(c => c != item); item = 'u'; query = query.Where(c => c != item); foreach (char c in query) Console.Write(c); // 只刪除了'u'----Not what yo might expect
2. 整理Linq to Objects中運(yùn)算符延遲計(jì)算特性
按字母順序整理:
1、具有延遲計(jì)算的運(yùn)算符
Cast,Concat,DefaultIfEmpty,Distinct,Except,GroupBy,GroupJoin,Intersect,Join,OfType,OrderBy,OrderByDescending ,Repeat,Reverse,Select,SelectMany,Skip,SkipWhile,Take,TakeWhile,ThenBy,ThenByDescending,Union,Where,Zip
2、立即執(zhí)行的運(yùn)算符
Aggregate,All,Any,Average,Contains,Count,ElementAt,ElementAtOrDefault,Empty,F(xiàn)irst,F(xiàn)irstOrDefault,Last,LastOrDefault ,LongCount,Max,Min,Range,SequenceEqual,Single,SingleOrDefault,Sum,ToArray,ToDictionary,ToList,ToLookup
注意:特殊的AsEnumerable運(yùn)算符,用于處理LINQ to Entities操作遠(yuǎn)程數(shù)據(jù)源,將IQueryable遠(yuǎn)程數(shù)據(jù)立即轉(zhuǎn)化為本地的IEnumerable集合。若AsEnumerable接收參數(shù)是IEnumerable內(nèi)存集合則什么都不做。
三、LINQ 和字符串
LINQ 可用于查詢和轉(zhuǎn)換字符串和字符串集合。它對(duì)文本文件中的半結(jié)構(gòu)化數(shù)據(jù)尤其有用。LINQ 查詢可與傳統(tǒng)的字符串函數(shù)和正則表達(dá)式結(jié)合使用。
例如:
- 可以使用 Split 或 Split 方法來創(chuàng)建字符串?dāng)?shù)組,然后可以使用 LINQ 來查詢或修改此數(shù)組;
- 可以在 LINQ 查詢的 where 子句中使用 IsMatch 方法;
- 可以使用 LINQ 來查詢或修改由正則表達(dá)式返回的 MatchCollection 結(jié)果
1、查詢文本塊
實(shí)例1: 統(tǒng)計(jì)單詞在字符串中出現(xiàn)的次數(shù)
請(qǐng)注意,若要執(zhí)行計(jì)數(shù),請(qǐng)先調(diào)用 Split 方法來創(chuàng)建詞數(shù)組。Split 方法存在性能開銷。如果對(duì)字符串執(zhí)行的唯一操作是計(jì)數(shù)詞,則應(yīng)考慮改用 Matches 或 IndexOf 方法。
string text = @"Historically, the world of data and the world of objects" + @" have not been well integrated. "; string searchTerm = "data"; //字符串轉(zhuǎn)換成數(shù)組 string[] source = text.Split(new char[] { '.', '?', '!', ' ', ';', ':', ',' }, StringSplitOptions.RemoveEmptyEntries); // 創(chuàng)建查詢,并忽略大小寫比較 var matchQuery = from word in source where word.ToLowerInvariant() == searchTerm.ToLowerInvariant() select word; // 統(tǒng)計(jì)匹配數(shù)量. int wordCount = matchQuery.Count(); Console.WriteLine("{0} occurrences(s) of the search term \"{1}\" were found.", wordCount, searchTerm); //1 occurrences(s) of the search term "data" were found.
實(shí)例2: 在文本文件中,找出包含一組指定單詞的的句子。
在此示例中,查詢將返回包含單詞“Historically,”、“data,”和“integrated”的句子。 雖然在此示例中搜索條件數(shù)組是硬編碼的,但也可以在運(yùn)行時(shí)動(dòng)態(tài)填充此數(shù)組。
string text = @"Historically, the world of data and the world of objects " + @"have not been well integrated. Programmers work in C# or Visual Basic " + @"and also in SQL or XQuery. On the one side are concepts such as classes, " + @"objects, fields, inheritance, and .NET Framework APIs. On the other side " + @"are tables, columns, rows, nodes, and separate languages for dealing with " + @"them. Data types often require translation between the two worlds; there are " + @"different standard functions. Because the object world has no notion of query, a " + @"query can only be represented as a string without compile-time type checking or " + @"IntelliSense support in the IDE. Transferring data from SQL tables or XML trees to " + @"objects in memory is often tedious and error-prone."; // 將文本塊切割成數(shù)組. string[] sentences = text.Split(new char[] { '.', '?', '!' }); // 定義搜索條件,此列表可以運(yùn)行時(shí)動(dòng)態(tài)添加. string[] wordsToMatch = { "Historically", "data", "integrated" }; // 去重,取交集后的數(shù)量對(duì)比 var sentenceQuery = from sentence in sentences let w = sentence.Split(new char[] { '.', '?', '!', ' ', ';', ':', ',' }, StringSplitOptions.RemoveEmptyEntries) where w.Distinct().Intersect(wordsToMatch).Count() == wordsToMatch.Count() select sentence; // 執(zhí)行這個(gè)查詢 foreach (string str in sentenceQuery) { Console.WriteLine(str); } //Historically, the world of data and the world of objects have not been well integrated
查詢運(yùn)行時(shí)首先將文本拆分成句子,然后將句子拆分成包含每個(gè)單詞的字符串?dāng)?shù)組。對(duì)于每個(gè)這樣的數(shù)組,Distinct 方法移除所有重復(fù)的單詞,然后查詢對(duì)單詞數(shù)組和 wordstoMatch 數(shù)組執(zhí)行 Intersect 操作。如果交集的計(jì)數(shù)與 wordsToMatch 數(shù)組的計(jì)數(shù)相同,則在單詞中找到了所有的單詞,且返回原始句子。
在對(duì) Split 的調(diào)用中,使用標(biāo)點(diǎn)符號(hào)作為分隔符,以從字符串中移除標(biāo)點(diǎn)符號(hào)。如果您沒有這樣做,則假如您有一個(gè)字符串“Historically,”,該字符串不會(huì)與 wordsToMatch 數(shù)組中的“Historically”相匹配。根據(jù)源文本中標(biāo)點(diǎn)的類型,您可能必須使用其他分隔符。
實(shí)例3: 查詢字符串中的字符
因?yàn)?String 類實(shí)現(xiàn)泛型 IEnumerable 接口,所以可以將任何字符串作為字符序列進(jìn)行查詢。但是,這不是 LINQ 的常見用法。若要執(zhí)行復(fù)雜的模式匹配操作,請(qǐng)使用 Regex 類。
以下示例查詢一個(gè)字符串以確定它所包含的數(shù)字?jǐn)?shù)量。 請(qǐng)注意,在第一次執(zhí)行此查詢后將“重用”此查詢。 這是可能的,因?yàn)椴樵儽旧聿⒉淮鎯?chǔ)任何實(shí)際的結(jié)果。
string aString = "ABCDE99F-J74-12-89A"; // 只選擇數(shù)字的字符 IEnumerable<char> stringQuery = from ch in aString where Char.IsDigit(ch) select ch; // 執(zhí)行這個(gè)查詢 foreach (char c in stringQuery) Console.Write(c + " "); //9 9 7 4 1 2 8 9 // 對(duì)上面的查詢調(diào)用Count方法 int count = stringQuery.Count(); Console.WriteLine("Count = {0}", count); //Count = 8 // 選擇第一個(gè)“-”之前的所有字符 IEnumerable<char> stringQuery2 = aString.TakeWhile(c => c != '-'); // 執(zhí)行第二個(gè)查詢 foreach (char c in stringQuery2) Console.Write(c); //ABCDE99F
實(shí)例4:用正則表達(dá)式結(jié)合 LINQ 查詢
此示例演示如何使用 Regex 類創(chuàng)建正則表達(dá)式以便在文本字符串中進(jìn)行更復(fù)雜的匹配。使用 LINQ 查詢可以方便地對(duì)您要用正則表達(dá)式搜索的文件進(jìn)行準(zhǔn)確篩選,以及對(duì)結(jié)果進(jìn)行加工。
//根據(jù)不同版本的 vs 修改路徑 string startFolder = @"C:\Program Files (x86)\Microsoft Visual Studio 11.0\"; IEnumerable fileList = Directory.GetFiles(startFolder, "*.*", SearchOption.AllDirectories).Select(p => new System.IO.FileInfo(p)); //創(chuàng)建正則表達(dá)式來尋找所有的"Visual" Regex searchTerm = new Regex(@"Visual (Basic|C#|C\+\+|Studio)"); //搜索每一個(gè)“.htm”文件 //通過 where 找到匹配項(xiàng) //注意:select 中的變量要求顯示聲明其類型,因?yàn)?MatchCollection 不是泛型 IEnumerable 集合 var queryMatchingFiles = from file in fileList where file.Extension == ".htm" let fileText = File.ReadAllText(file.FullName) let matches = searchTerm.Matches(fileText) where matches.Count > 0 select new { name = file.FullName, matchedValues = from Match match in matches select match.Value }; foreach (var v in queryMatchingFiles) { //修剪匹配找到的文件中的路徑 string s = v.name.Substring(startFolder.Length - 1); Console.WriteLine(s); //輸出找到的匹配值 foreach (var v2 in v.matchedValues) { Console.WriteLine(" " + v2); } } //\professional\2052\License.htm // Visual Studio // Visual Studio // Visual Studio //\VB\VBWizards\FrameSetTemplates\1033\BanToc.htm // Visual Studio
還可以查詢由 RegEx 搜索返回的 MatchCollection 對(duì)象。在此示例中,結(jié)果中僅生成每個(gè)匹配項(xiàng)的值。但也可使用 LINQ 對(duì)該集合執(zhí)行各種篩選、排序和分組操作。
【注意】由于 MatchCollection 是非泛型 IEnumerable 集合,因此必須顯式聲明查詢中的范圍變量的類型。
2、查詢文本格式的半結(jié)構(gòu)化數(shù)據(jù)
許多不同類型的文本文件都包含一系列行,通常具有類似的格式設(shè)置,例如制表符分隔或逗號(hào)分隔的文件或固定長(zhǎng)度的行。
將此類文本文件讀入內(nèi)存后,可以使用 LINQ 來查詢和/或修改其中的行。 LINQ 查詢還簡(jiǎn)化了合并來自多個(gè)源的數(shù)據(jù)的任務(wù)。
實(shí)例1、如何查找兩個(gè)集合間的差異
此示例演示如何使用 LINQ 對(duì)兩個(gè)字符串列表進(jìn)行比較,并輸出那些位于 names1.txt 中但不在 names2.txt 中的行。
names1.txt
Bankov, Peter
Holm, Michael
Garcia, Hugo
Potra, Cristina
Noriega, Fabricio
Aw, Kam Foo
Beebe, Ann
Toyoshima, Tim
Guy, Wey Yuan
Garcia, Debra
names2.txt
Liu, Jinghao
Bankov, Peter
Holm, Michael
Garcia, Hugo
Beebe, Ann
Gilchrist, Beth
Myrcha, Jacek
Giakoumakis, Leo
McLin, Nkenge
El Yassir, Mehdi
代碼:
//創(chuàng)建數(shù)據(jù)源 var names1Text = File.ReadAllLines(@"names1.txt"); var names2Text = File.ReadAllLines(@"names2.txt"); //創(chuàng)建查詢,這里必須使用方法語法 var query = names1Text.Except(names2Text); //執(zhí)行查詢 Console.WriteLine("The following lines are in names1.txt but not names2.txt"); foreach (var name in query) { Console.WriteLine(name); }
【注意】某些類型的查詢操作(如 Except、Distinct、Union 和 Concat)只能用基于方法的語法表示。
實(shí)例2: 根據(jù)某字段進(jìn)行排序或過濾文本行
下面的示例演示如何按結(jié)構(gòu)化文本(如逗號(hào)分隔值)行中的任意字段對(duì)該文本行進(jìn)行排序。可在運(yùn)行時(shí)動(dòng)態(tài)指定該字段。此示例還演示如何從方法返回查詢變量。
scores.csv:假定 scores.csv 中的字段表示學(xué)生的 ID 號(hào),后面跟四個(gè)測(cè)驗(yàn)分?jǐn)?shù)。
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
// 創(chuàng)建數(shù)據(jù)源 string[] scores = System.IO.File.ReadAllLines(@"scores.csv"); // 可以改為 0~4 的任意值 int sortField = 1; Console.WriteLine("Sorted highest to lowest by field [{0}]:", sortField); //演示從方法返回查詢,這里執(zhí)行查詢 foreach (string str in RunQuery(scores, sortField)) { Console.WriteLine(str); } // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit"); // 返回查詢變量,非查詢結(jié)果 static IEnumerable<string> RunQuery(IEnumerable<string> source, int num) { // 分割字符串來排序 var scoreQuery = from line in source let fields = line.Split(',') orderby fields[num] descending select line; return scoreQuery; } /* Output (if sortField == 1): Sorted highest to lowest by field [1]: 116, 99, 86, 90, 94 120, 99, 82, 81, 79 111, 97, 92, 81, 60 114, 97, 89, 85, 82 121, 96, 85, 91, 60 122, 94, 92, 91, 91 117, 93, 92, 80, 87 118, 92, 90, 83, 78 113, 88, 94, 65, 91 112, 75, 84, 91, 39 119, 68, 79, 88, 92 115, 35, 72, 91, 70 */
實(shí)例3、如何對(duì)一個(gè)分割的文件的字段重新排序
逗號(hào)分隔值 (CSV) 文件是一種文本文件,通常用于存儲(chǔ)電子表格數(shù)據(jù)或其他由行和列表示的表格數(shù)據(jù)。通過使用 Split 方法分隔字段,可以非常輕松地使用 LINQ 來查詢和操作 CSV 文件。事實(shí)上,可以使用此技術(shù)來重新排列任何結(jié)構(gòu)化文本行部分;此技術(shù)不局限于 CSV 文件。
在下面的示例中,假定有三列分別代表學(xué)生的“姓氏”、“名字”和“ID”。這些字段基于學(xué)生的姓氏按字母順序排列。查詢生成一個(gè)新序列,其中首先出現(xiàn)的是 ID 列,后面的第二列組合了學(xué)生的名字和姓氏。根據(jù) ID 字段重新排列各行。結(jié)果保存到新文件,但不修改原始數(shù)據(jù)。
spreadsheet1.csv
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
代碼
//數(shù)據(jù)源 var lines = File.ReadAllLines(@"spreadsheet1.csv"); //將舊數(shù)據(jù)的第2列的字段放到第一位,逆向結(jié)合第0列和第1列的字段 var query = from line in lines let t = line.Split(',') orderby t[2] select $"{t[2]}, {t[1]} {t[0]}"; foreach (var q in query) { Console.WriteLine(q); } //寫入文件 File.WriteAllLines("spreadsheet2.csv", query);
實(shí)例4、如何組合和比較字符串集合
此示例演示如何合并包含文本行的文件,然后排序結(jié)果。具體來說,此示例演示如何對(duì)兩組文本行執(zhí)行簡(jiǎn)單的串聯(lián)、聯(lián)合和交集。
names1.txt
Bankov, Peter
Holm, Michael
Garcia, Hugo
Potra, Cristina
Noriega, Fabricio
Aw, Kam Foo
Beebe, Ann
Toyoshima, Tim
Guy, Wey Yuan
Garcia, Debra
names2.txt
Liu, Jinghao
Bankov, Peter
Holm, Michael
Garcia, Hugo
Beebe, Ann
Gilchrist, Beth
Myrcha, Jacek
Giakoumakis, Leo
McLin, Nkenge
El Yassir, Mehdi
代碼:
var names1Text = File.ReadAllLines(@"names1.txt"); var names2Text = File.ReadAllLines(@"names2.txt"); //簡(jiǎn)單連接,并排序。重復(fù)保存。 var concatQuery = names1Text.Concat(names2Text).OrderBy(x => x); OutputQueryResult(concatQuery, "Simple concatenate and sort. Duplicates are preserved:"); //基于默認(rèn)字符串比較器連接,并刪除重復(fù)。 var unionQuery = names1Text.Union(names2Text).OrderBy(x => x); OutputQueryResult(unionQuery, "Union removes duplicate names:"); //查找在兩個(gè)文件中出現(xiàn)的名稱 var intersectQuery = names1Text.Intersect(names2Text).OrderBy(x => x); OutputQueryResult(intersectQuery, "Merge based on intersect:"); //在每個(gè)列表中找到匹配的字段。使用 concat 將兩個(gè)結(jié)果合并,然后使用默認(rèn)的字符串比較器進(jìn)行排序 const string nameMatch = "Garcia"; var matchQuery1 = from name in names1Text let t = name.Split(',') where t[0] == nameMatch select name; var matchQuery2 = from name in names2Text let t = name.Split(',') where t[0] == nameMatch select name; var temp = matchQuery1.Concat(matchQuery2).OrderBy(x => x); OutputQueryResult(temp, $"Concat based on partial name match \"{nameMatch}\":"); private static void OutputQueryResult(IEnumerable<string> querys, string title) { Console.WriteLine(Environment.NewLine + title); foreach (var query in querys) { Console.WriteLine(query); } Console.WriteLine($"{querys.Count()} total names in list"); }
實(shí)例5:從多個(gè)源填充對(duì)象集合
不要嘗試將內(nèi)存中的數(shù)據(jù)或文件系統(tǒng)中的數(shù)據(jù)與仍在數(shù)據(jù)庫(kù)中的數(shù)據(jù)相聯(lián)接。此種跨域聯(lián)接會(huì)生成未定義的結(jié)果,因?yàn)閿?shù)據(jù)庫(kù)查詢和其他類型的源定義聯(lián)接運(yùn)算的方式可能不同。另外,如果數(shù)據(jù)庫(kù)中的數(shù)據(jù)量足夠大,則存在此類運(yùn)算引發(fā)內(nèi)存不足異常的風(fēng)險(xiǎn)。
若要將數(shù)據(jù)庫(kù)數(shù)據(jù)與內(nèi)存中的數(shù)據(jù)相聯(lián)接,請(qǐng)首先對(duì)數(shù)據(jù)庫(kù)查詢調(diào)用 ToList 或 ToArray,然后對(duì)返回的集合執(zhí)行聯(lián)接。
//每行 names.csv 包含姓氏,名字,和身份證號(hào),以逗號(hào)分隔。例如,Omelchenko,Svetlana,111 var names = File.ReadAllLines(@"names.csv"); //每行 scores.csv 包括身份證號(hào)碼和四個(gè)測(cè)試評(píng)分,以逗號(hào)分隔。例如,111,97,92,81,60 var scores = File.ReadAllLines(@"scores.csv"); //使用一個(gè)匿名的類型合并數(shù)據(jù)源。 //【注意】動(dòng)態(tài)創(chuàng)建一個(gè) int 的考試成績(jī)成員列表。 //跳過分割字符串中的第一項(xiàng),因?yàn)樗菍W(xué)生的身份證,不是一個(gè)考試成績(jī) var students = from name in names let t = name.Split(',') from score in scores let t2 = score.Split(',') where t[2] == t2[0] select new { FirstName = t[0], LastName = t[1], ID = Convert.ToInt32(t[2]), ExamScores = (from scoreAsText in t2.Skip(1) select Convert.ToInt32(scoreAsText)).ToList() }; //顯示每個(gè)學(xué)生的名字和考試平均成績(jī)。 foreach (var student in students) { Console.WriteLine( $"The average score of {student.FirstName} {student.LastName} is {student.ExamScores.Average()}."); }
實(shí)例6、如何向不同的文件中加入內(nèi)容
此示例演示如何聯(lián)接兩個(gè)逗號(hào)分隔文件中的數(shù)據(jù),這兩個(gè)文件共享一個(gè)用作匹配鍵的共同值。如果您必須將兩個(gè)電子表格的數(shù)據(jù)或一個(gè)電子表格和一個(gè)其他格式的文件的數(shù)據(jù)組合為一個(gè)新文件,則此技術(shù)很有用。還可以修改此示例以適合任意種類的結(jié)構(gòu)化文本。
names.csv:此文件表示一個(gè)電子表格。該電子表格包含學(xué)生的姓氏、名字和學(xué)生 ID。
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
scores.csv:此文件表示電子表格數(shù)據(jù)。第 1 列是學(xué)生的 ID,第 2 至 5 列是測(cè)驗(yàn)分?jǐn)?shù)。
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
代碼:
var names = File.ReadAllLines(@"names.csv"); var scores = File.ReadAllLines(@"scores.csv"); //Name: Last[0], First[1], ID[2] // Omelchenko, Svetlana, 11 //Score: StudentID[0], Exam1[1] Exam2[2], Exam3[3], Exam4[4] // 111, 97, 92, 81, 60 //該查詢基于 id 連接兩個(gè)不同的電子表格 var query = from name in names let t1 = name.Split(',') from score in scores let t2 = score.Split(',') where t1[2] == t2[0] orderby t1[0] select $"{t1[0]},{t2[1]},{t2[2]},{t2[3]},{t2[4]}"; //輸出 OutputQueryResult(query, "Merge two spreadsheets:"); private static void OutputQueryResult(IEnumerable<string> querys, string title) { Console.WriteLine(Environment.NewLine + title); foreach (var query in querys) { Console.WriteLine(query); } Console.WriteLine($"{querys.Count()} total names in list"); }
實(shí)例7:如何使用 group 將一個(gè)文件拆分成多個(gè)文件
此示例演示一種進(jìn)行以下操作的方法:合并兩個(gè)文件的內(nèi)容,然后創(chuàng)建一組以新方式組織數(shù)據(jù)的新文件。
name1.txt
Bankov, Peter
Holm, Michael
Garcia, Hugo
Potra, Cristina
Noriega, Fabricio
Aw, Kam Foo
Beebe, Ann
Toyoshima, Tim
Guy, Wey Yuan
Garcia, Debra
name2.text
Liu, Jinghao
Bankov, Peter
Holm, Michael
Garcia, Hugo
Beebe, Ann
Gilchrist, Beth
Myrcha, Jacek
Giakoumakis, Leo
McLin, Nkenge
El Yassir, Mehdi
name2.txt
Liu, Jinghao
Bankov, Peter
Holm, Michael
Garcia, Hugo
Beebe, Ann
Gilchrist, Beth
Myrcha, Jacek
Giakoumakis, Leo
McLin, Nkenge
El Yassir, Mehdi
代碼:
var fileA = File.ReadAllLines(@"names1.txt"); var fileB = File.ReadAllLines(@"names2.txt"); //并集:連接并刪除重復(fù)的名字 var mergeQuery = fileA.Union(fileB); //根據(jù)姓氏的首字母對(duì)姓名進(jìn)行分組 var query = from name in mergeQuery let t = name.Split(',') group name by t[0][0] into g orderby g.Key select g; //為創(chuàng)建的每個(gè)組創(chuàng)建一個(gè)新文件,請(qǐng)注意,需要使用嵌套的foreach循環(huán)來訪問每個(gè)組的單個(gè)項(xiàng)。 foreach (var g in query) { var fileName = @"testFile_" + g.Key + ".txt"; Console.WriteLine(g.Key + ":"); //寫入文件 using (var sw = new StreamWriter(fileName)) { foreach (var name in g) { sw.WriteLine(name); Console.WriteLine(" " + name); } } }
對(duì)于與數(shù)據(jù)文件位于同一文件夾中的每個(gè)組,程序?qū)檫@些組編寫單獨(dú)的文件。
實(shí)例8、如何計(jì)算一個(gè) CSV 文本文件中的列值
此示例演示如何對(duì) .csv 文件的列執(zhí)行諸如 Sum、Average、Min 和 Max 等聚合計(jì)算。此處所示的示例原則可以應(yīng)用于其他類型的結(jié)構(gòu)化文本。
scores.csv
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
scores.csv:假定第一列表示學(xué)員 ID,后面幾列表示四次考試的分?jǐn)?shù)。
var scores = File.ReadAllLines(@"scores.csv"); //指定要計(jì)算的列 const int examNum = 3; //scores.csv 格式: //Student ID Exam#1 Exam#2 Exam#3 Exam#4 //111, 97, 92, 81, 60 //+1 表示跳過第一列 //計(jì)算單一列 SingleColumn(scores, examNum + 1); Console.WriteLine(); //計(jì)算多列 MultiColumns(scores); private static void SingleColumn(IEnumerable<string> strs, int examNum) { Console.WriteLine("Single Column Query:"); //查詢分兩步: // 1.分割字符串 // 2.對(duì)要計(jì)算的列的值轉(zhuǎn)換為 int var query = from str in strs let t = str.Split(',') select Convert.ToInt32(t[examNum]); //對(duì)指定的列進(jìn)行統(tǒng)計(jì) var average = query.Average(); var max = query.Max(); var min = query.Min(); Console.WriteLine($"Exam #{examNum}: Average:{average:##.##} High Score:{max} Low Score:{min}"); } private static void MultiColumns(IEnumerable<string> strs) { Console.WriteLine("Multi Column Query:"); //查詢步驟: // 1.分割字符串 // 2.跳過 id 列(第一列) // 3.將當(dāng)前行的每個(gè)評(píng)分都轉(zhuǎn)換成 int,并選擇整個(gè)序列作為一行結(jié)果。 var query = from str in strs let t1 = str.Split(',') let t2 = t1.Skip(1) select (from t in t2 select Convert.ToInt32(t)); //執(zhí)行查詢并緩存結(jié)果以提高性能 var results = query.ToList(); //找出結(jié)果的列數(shù) var count = results[0].Count(); //執(zhí)行統(tǒng)計(jì) //為每一列分?jǐn)?shù)的循環(huán)執(zhí)行一次循環(huán) for (var i = 0; i < count; i++) { var query2 = from result in results select result.ElementAt(i); var average = query2.Average(); var max = query2.Max(); var min = query2.Min(); //+1 因?yàn)?#1 表示第一次考試 Console.WriteLine($"Exam #{i + 1} Average: {average:##.##} High Score: {max} Low Score: {min}"); } }
查詢的工作原理是使用 Split 方法將每一行文本轉(zhuǎn)換為數(shù)組。每個(gè)數(shù)組元素表示一列。最后,每一列中的文本都轉(zhuǎn)換為其數(shù)字表示形式。如果文件是制表符分隔文件,只需將 Split 方法中的參數(shù)更新為 \t。
實(shí)例9、從 CSV 文件生成 XML
下面的代碼對(duì)字符串?dāng)?shù)組執(zhí)行 LINQ 查詢。
該查詢使用 let
子句將每個(gè)字符串分隔成字段數(shù)組。
// Create the text file. string csvString = @"GREAL,Great Lakes Food Market,Howard Snyder,Marketing Manager,(503) 555-7555,2732 Baker Blvd.,Eugene,OR,97403,USA HUNGC,Hungry Coyote Import Store,Yoshi Latimer,Sales Representative,(503) 555-6874,City Center Plaza 516 Main St.,Elgin,OR,97827,USA LAZYK,Lazy K Kountry Store,John Steel,Marketing Manager,(509) 555-7969,12 Orchestra Terrace,Walla Walla,WA,99362,USA LETSS,Let's Stop N Shop,Jaime Yorres,Owner,(415) 555-5938,87 Polk St. Suite 5,San Francisco,CA,94117,USA"; File.WriteAllText("cust.csv", csvString); // Read into an array of strings. string[] source = File.ReadAllLines("cust.csv"); XElement cust = new XElement("Root", from str in source let fields = str.Split(',') select new XElement("Customer", new XAttribute("CustomerID", fields[0]), new XElement("CompanyName", fields[1]), new XElement("ContactName", fields[2]), new XElement("ContactTitle", fields[3]), new XElement("Phone", fields[4]), new XElement("FullAddress", new XElement("Address", fields[5]), new XElement("City", fields[6]), new XElement("Region", fields[7]), new XElement("PostalCode", fields[8]), new XElement("Country", fields[9]) ) ) ); Console.WriteLine(cust);
到此這篇關(guān)于C#中LINQ to Objects的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C#下實(shí)現(xiàn)創(chuàng)建和刪除目錄的實(shí)例代碼
這篇文章主要介紹了C#下實(shí)現(xiàn)創(chuàng)建和刪除目錄的方法,功能非常實(shí)用,需要的朋友可以參考下2014-08-08C#使用Socket實(shí)現(xiàn)發(fā)送和接收?qǐng)D片的方法
這篇文章主要介紹了C#使用Socket實(shí)現(xiàn)發(fā)送和接收?qǐng)D片的方法,涉及C#操作socket發(fā)送與接收文件的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04C#對(duì)list列表進(jìn)行隨機(jī)排序的方法
這篇文章主要介紹了C#對(duì)list列表進(jìn)行隨機(jī)排序的方法,涉及C#操作list列表的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04C++聯(lián)合體轉(zhuǎn)換成C#結(jié)構(gòu)的實(shí)現(xiàn)方法
這篇文章主要介紹了C++聯(lián)合體轉(zhuǎn)換成C#結(jié)構(gòu)的實(shí)現(xiàn)方法,需要的朋友可以參考下2014-08-08C#中逆變的實(shí)際應(yīng)用場(chǎng)景詳解
在好多的.net的書籍中都看到過逆變和協(xié)變的概念,也在網(wǎng)上搜了一些關(guān)于這兩個(gè)概念的解釋,但是一直感覺似懂非懂的,直到最近在項(xiàng)目中實(shí)際遇到了一個(gè)問題,恰好用到了逆變,下面這篇文章主要給大家介紹了關(guān)于C#中逆變的實(shí)際應(yīng)用場(chǎng)景,需要的朋友可以參考下2022-01-01C#遍歷文件夾及其子目錄的完整實(shí)現(xiàn)方法
這篇文章主要介紹了C#遍歷文件夾及其子目錄的方法,涉及C#文件與目錄的基本操作技巧,簡(jiǎn)單實(shí)用,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06