天天看點

[C#] Linq To Objects - 如何操作字元串

Linq To Objects - 如何操作字元串

  開篇語:  

  上次釋出的 《LINQ:進階 - LINQ 标準查詢操作概述》(90+贊) 社會反響不錯,但自己卻始終覺得缺點什麼!“紙上得來終覺淺,絕知此事要躬行”,沒錯,就是實戰!這次讓我們一起來看看一些操作字元串的技巧,也許能引我們從不同的角度思考問題,進而走出思維的死角!

  LINQ 可用于查詢和轉換字元串和字元串集合。它對文本檔案中的半結構化資料尤其有用。LINQ 查詢可與傳統的字元串函數和正規表達式結合使用。

  例如,可以使用 Split 或 Split 方法來建立字元串數組,然後可以使用 LINQ 來查詢或修改此數組;可以在 LINQ 查詢的 where 子句中使用 IsMatch 方法;可以使用 LINQ 來查詢或修改由正規表達式傳回的 MatchCollection 結果......

查詢文本塊

  可以通過使用 Split 方法或 Split 方法将文本塊拆分成可查詢的較小字元串數組,來查詢、分析和修改文本塊。可以将源文本拆分成詞語、句子、段落、頁或任何其他條件,然後根據查詢的需要,執行其他拆分。  

  (1)如何統計單詞在字元串出現次數?

                    --示範如何使用 LINQ 對文本進行簡單查詢

  (2)如何查詢包含一組指定的單詞的句子?

                    --示範如何按任意邊界拆分文本檔案,以及如何對每個部分執行查詢

  (3)如何在字元串中查詢字元?

                    --示範字元串是可查詢類型

  (4)如何用正規表達式結合 LINQ 查詢?

                    --示範如何在 LINQ 查詢中使用正規表達式來對篩選的查詢結果執行複雜模式比對

查詢文本格式的半結構化資料

   許多不同類型的文本檔案都包含一系列行,通常具有類似的格式,例如制表符分隔或逗号分隔檔案或固定長度的行。在将此類文本檔案讀取到記憶體中後,可以使用 LINQ 來查詢和/或修改行。LINQ 查詢還簡化了組合多個源的資料的任務。  

  (5)如何查找兩個集合間的差異?

                    --示範如何查找位于一個清單中但不在另一個清單中的所有字元串

  (6)如何排序或過濾任意詞語或字段的文本資料?

                    --示範如何根據任意詞語或字段排序文本行

  (7)如何對一個分割的檔案的字段重新排序?

                    --示範如何重新排列 .csv 檔案中的某一行中的字段

  (8)如何組合和比較字元串集合?

                    --示範如何以各種方式組合字元串清單

  (9)如何從多個源填充對象集合?

                    --示範如何将多個文本檔案用作資料源來建立對象集合

  (10)如何使用 group 将一個檔案拆分成多個檔案?

                    --示範如何将單個檔案用作資料源來建立新檔案

  (11)如何向不同的檔案中加入内容?

                    --示範如何使用比對鍵将兩個清單中的字元串組合為單個字元串

  (12)如何計算一個 CSV 文本檔案中的列值?

                    --示範如何對 .csv 檔案中的文本資料執行數學運算

一、如何統計單詞在字元串出現次數 

  請注意,若要執行計數,請先調用 Split 方法來建立詞數組。Split 方法存在性能開銷。如果對字元串執行的唯一操作是計數詞,則應考慮改用 Matches 或 IndexOf 方法。

1             const string text = @"Historically, the world of data and the world of objects" +
 2                                 @" have not been well integrated. Programmers work in C# or Visual Basic" +
 3                                 @" and also in SQL or XQuery. On the one side are concepts such as classes," +
 4                                 @" objects, fields, inheritance, and .NET Framework APIs. On the other side" +
 5                                 @" are tables, columns, rows, nodes, and separate languages for dealing with" +
 6                                 @" them. Data types often require translation between the two worlds; there are" +
 7                                 @" different standard functions. Because the object world has no notion of query, a" +
 8                                 @" query can only be represented as a string without compile-time type checking or" +
 9                                 @" IntelliSense support in the IDE. Transferring data from SQL tables or XML trees to" +
10                                 @" objects in memory is often tedious and error-prone.";
11 
12             const string searchTerm = "data";
13 
14             //字元串轉換成數組
15             var source = text.Split(new[] { '.', '?', '!', ' ', ';', ':', ',' }, StringSplitOptions.RemoveEmptyEntries);
16 
17             //建立查詢,并忽略大小寫比較
18             var matchQuery = from word in source
19                              where string.Equals(word, searchTerm, StringComparison.InvariantCultureIgnoreCase)
20                              select word;
21 
22             //統計比對數量
23             var wordCount = matchQuery.Count();
24             Console.WriteLine($"{wordCount} occurrences(s) of the search term \"{searchTerm}\" were found.");      
[C#] Linq To Objects - 如何操作字元串

二、如何查詢包含指定的一組單詞的句子

  此示例示範如何查找文本檔案中包含指定的一組單詞中每個單詞的比對項的句子。雖然在此示例中搜尋條件數組是寫死的,但也可以在運作時動态填充此數組。

1             const string text = @"Historically, the world of data and the world of objects " +
 2                                 @"have not been well integrated. Programmers work in C# or Visual Basic " +
 3                                 @"and also in SQL or XQuery. On the one side are concepts such as classes, " +
 4                                 @"objects, fields, inheritance, and .NET Framework APIs. On the other side " +
 5                                 @"are tables, columns, rows, nodes, and separate languages for dealing with " +
 6                                 @"them. Data types often require translation between the two worlds; there are " +
 7                                 @"different standard functions. Because the object world has no notion of query, a " +
 8                                 @"query can only be represented as a string without compile-time type checking or " +
 9                                 @"IntelliSense support in the IDE. Transferring data from SQL tables or XML trees to " +
10                                 @"objects in memory is often tedious and error-prone.";
11 
12             //将文本塊切割成數組
13             var sentences = text.Split('.', '?', '!');
14 
15             //定義搜尋條件,此清單可以運作時動态添加
16             string[] wordsToMatch = { "Historically", "data", "integrated" };
17 
18             var match = from sentence in sentences
19                         let t =
20                             sentence.Split(new char[] { '.', '?', '!', ' ', ';', ':', ',' }, StringSplitOptions.RemoveEmptyEntries)
21                         where t.Distinct().Intersect(wordsToMatch).Count() == wordsToMatch.Length   //去重,取交集後的數量對比
22                         select sentence;
23 
24             foreach (var s in match)
25             {
26                 Console.WriteLine(s);
27             }      
[C#] Linq To Objects - 如何操作字元串

  

  查詢運作時首先将文本拆分成句子,然後将句子拆分成包含每個單詞的字元串數組。對于每個這樣的數組,Distinct<TSource> 方法移除所有重複的單詞,然後查詢對單詞數組和 wordstoMatch 數組執行 Intersect<TSource> 操作。如果交集的計數與 wordsToMatch 數組的計數相同,則在單詞中找到了所有的單詞,且傳回原始句子。

  在對 Split 的調用中,使用标點符号作為分隔符,以從字元串中移除标點符号。如果您沒有這樣做,則假如您有一個字元串“Historically,”,該字元串不會與 wordsToMatch 數組中的“Historically”相比對。根據源文本中标點的類型,您可能必須使用其他分隔符。

三、如何在字元串中查詢字元

  因為 String 類實作泛型 IEnumerable<T> 接口,是以可以将任何字元串作為字元序列進行查詢。但是,這不是 LINQ 的常見用法。若要執行複雜的模式比對操作,請使用 Regex 類。  

  下面的示例查詢一個字元串以确定它包含的數字的數目。

1             const string aString = "ABCDE99F-J74-12-89A";
 2 
 3             //隻選擇數字的字元 
 4             var digits = from ch in aString
 5                          where char.IsDigit(ch)
 6                          select ch;
 7 
 8             Console.Write("digit: ");
 9 
10             foreach (var n in digits)
11             {
12                 Console.Write($"{n} ");
13             }
14 
15             Console.WriteLine();
16 
17             //選擇第一個“-”之前的所有字元
18             var query = aString.TakeWhile(x => x != '-');
19 
20             foreach (var ch in query)
21             {
22                 Console.Write(ch);
23             }      
[C#] Linq To Objects - 如何操作字元串

四、如何用正規表達式結合 LINQ 查詢

  此示例示範如何使用 Regex 類建立正規表達式以便在文本字元串中進行更複雜的比對。使用 LINQ 查詢可以友善地對您要用正規表達式搜尋的檔案進行準确篩選,以及對結果進行加工。  

1             //根據不同版本的 vs 修改路徑
 2             const string floder = @"C:\Program Files (x86)\Microsoft Visual Studio 14.0\";
 3             var infoes = GetFiles(floder);
 4             //建立正規表達式來尋找所有的"Visual"
 5             var searchTerm = new Regex(@"Visual (Basic|C#|C\+\+|J#|SourceSafe|Studio)");
 6 
 7             //搜尋每一個“.html”檔案
 8             //通過 where 找到比對項
 9             //【注意】select 中的變量要求顯示聲明其類型,因為 MatchCollection 不是泛型 IEnumerable 集合
10             var query = from fileInfo in infoes
11                         where fileInfo.Extension == ".html"
12                         let text = File.ReadAllText(fileInfo.FullName)
13                         let matches = searchTerm.Matches(text)
14                         where matches.Count > 0
15                         select new
16                         {
17                             name = fileInfo.FullName,
18                             matchValue = from Match match in matches select match.Value
19                         };
20 
21             Console.WriteLine($"The term \"{searchTerm}\" was found in:");
22 
23             foreach (var q in query)
24             {
25                 //修剪比對找到的檔案中的路徑
26                 Console.WriteLine($"{q.name.Substring(floder.Length - 1)}");
27 
28                 //輸出找到的比對值
29                 foreach (var v in q.matchValue)
30                 {
31                     Console.WriteLine(v);
32                 }
33             }      
1         private static IList<FileInfo> GetFiles(string path)
2         {
3             var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
4 
5             return files.Select(file => new FileInfo(file)).ToList();
6         }      
[C#] Linq To Objects - 如何操作字元串

  您還可以查詢由 RegEx 搜尋傳回的 MatchCollection 對象。在此示例中,結果中僅生成每個比對項的值。但也可使用 LINQ 對該集合執行各種篩選、排序和分組操作。

  【注意】由于 MatchCollection 是非泛型 IEnumerable 集合,是以必須顯式聲明查詢中的範圍變量的類型。

五、如何查找兩個集合間的差異

  此示例示範如何使用 LINQ 對兩個字元串清單進行比較,并輸出那些位于 names1.txt 中但不在 names2.txt 中的行。

[C#] Linq To Objects - 如何操作字元串
[C#] Linq To Objects - 如何操作字元串
Bankov, Peter
Holm, Michael
Garcia, Hugo
Potra, Cristina
Noriega, Fabricio
Aw, Kam Foo
Beebe, Ann
Toyoshima, Tim
Guy, Wey Yuan
Garcia, Debra      

names1.txt

[C#] Linq To Objects - 如何操作字元串
[C#] Linq To Objects - 如何操作字元串
Liu, Jinghao
Bankov, Peter
Holm, Michael
Garcia, Hugo
Beebe, Ann
Gilchrist, Beth
Myrcha, Jacek
Giakoumakis, Leo
McLin, Nkenge
El Yassir, Mehdi      

names2.txt

1             //建立資料源
 2             var names1Text = File.ReadAllLines(@"names1.txt");
 3             var names2Text = File.ReadAllLines(@"names2.txt");
 4 
 5             //建立查詢,這裡必須使用方法文法
 6             var query = names1Text.Except(names2Text);
 7 
 8             //執行查詢
 9             Console.WriteLine("The following lines are in names1.txt but not names2.txt");
10             foreach (var name in query)
11             {
12                 Console.WriteLine(name);
13             }          
[C#] Linq To Objects - 如何操作字元串

  【注意】某些類型的查詢操作(如 Except<TSource>、Distinct<TSource>、Union<TSource> 和 Concat<TSource>)隻能用基于方法的文法表示。

六、如何排序或過濾任意單詞或字段的文本資料

  下面的示例示範如何按結構化文本(如逗号分隔值)行中的任意字段對該文本行進行排序。可在運作時動态指定該字段。假定 scores.csv 中的字段表示學生的 ID 号,後面跟四個測驗分數。  

[C#] Linq To Objects - 如何操作字元串
[C#] Linq To Objects - 如何操作字元串
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

1             //建立資料源
 2             var scores = File.ReadAllLines(@"scores.csv");
 3             //可以改為 0~4 的任意值
 4             const int sortField = 1;
 5 
 6             //示範從方法傳回查詢
 7             //傳回查詢變量,非查詢結果
 8             //這裡執行查詢
 9             foreach (var score in RunQuery(scores, sortField))
10             {
11                 Console.WriteLine(score);
12             }      
1         private static IEnumerable<string> RunQuery(IEnumerable<string> score, int num)
 2         {
 3             //分割字元串來排序
 4             var query = from line in score
 5                         let fields = line.Split(',')
 6                         orderby fields[num] descending
 7                         select line;
 8 
 9             return query;
10         }      
[C#] Linq To Objects - 如何操作字元串

  此示例還示範如何從方法傳回查詢變量。

七、如何對一個分割的檔案的字段重新排序

  逗号分隔值 (CSV) 檔案是一種文本檔案,通常用于存儲電子表格資料或其他由行和清單示的表格資料。通過使用 Split 方法分隔字段,可以非常輕松地使用 LINQ 來查詢和操作 CSV 檔案。事實上,可以使用此技術來重新排列任何結構化文本行部分;此技術不局限于 CSV 檔案。  

  在下面的示例中,假定有三列分别代表學生的“姓氏”、“名字”和“ID”。這些字段基于學生的姓氏按字母順序排列。查詢生成一個新序列,其中首先出現的是 ID 列,後面的第二列組合了學生的名字和姓氏。根據 ID 字段重新排列各行。結果儲存到新檔案,但不修改原始資料。  

[C#] Linq To Objects - 如何操作字元串
[C#] Linq To Objects - 如何操作字元串
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      

spreadsheet1.csv

1             //資料源
 2             var lines = File.ReadAllLines(@"spreadsheet1.csv");
 3             //将舊資料的第2列的字段放到第一位,逆向結合第0列和第1列的字段
 4             var query = from line in lines
 5                         let t = line.Split(',')
 6                         orderby t[2]
 7                         select $"{t[2]}, {t[1]} {t[0]}";
 8 
 9             foreach (var q in query)
10             {
11                 Console.WriteLine(q);
12             }
13 
14             //寫入檔案
15             File.WriteAllLines("spreadsheet2.csv", query);      
[C#] Linq To Objects - 如何操作字元串

八、如何組合和比較字元串集合

  此示例示範如何合并包含文本行的檔案,然後排序結果。具體來說,此示例示範如何對兩組文本行執行簡單的串聯、聯合和交集。  

[C#] Linq To Objects - 如何操作字元串
[C#] Linq To Objects - 如何操作字元串
Bankov, Peter
Holm, Michael
Garcia, Hugo
Potra, Cristina
Noriega, Fabricio
Aw, Kam Foo
Beebe, Ann
Toyoshima, Tim
Guy, Wey Yuan
Garcia, Debra      
[C#] Linq To Objects - 如何操作字元串
[C#] Linq To Objects - 如何操作字元串
Liu, Jinghao
Bankov, Peter
Holm, Michael
Garcia, Hugo
Beebe, Ann
Gilchrist, Beth
Myrcha, Jacek
Giakoumakis, Leo
McLin, Nkenge
El Yassir, Mehdi      
1             var names1Text = File.ReadAllLines(@"names1.txt");
 2             var names2Text = File.ReadAllLines(@"names2.txt");
 3 
 4             //簡單連接配接,并排序。重複儲存。 
 5             var concatQuery = names1Text.Concat(names2Text).OrderBy(x => x);
 6             OutputQueryResult(concatQuery, "Simple concatenate and sort. Duplicates are preserved:");
 7 
 8             //基于預設字元串比較器連接配接,并删除重名。 
 9             var unionQuery = names1Text.Union(names2Text).OrderBy(x => x);
10             OutputQueryResult(unionQuery, "Union removes duplicate names:");
11 
12             //查找在兩個檔案中出現的名稱 
13             var intersectQuery = names1Text.Intersect(names2Text).OrderBy(x => x);
14             OutputQueryResult(intersectQuery, "Merge based on intersect:");
15 
16             //在每個清單中找到比對的字段。使用 concat 将兩個結果合并,然後使用預設的字元串比較器進行排序
17             const string nameMatch = "Garcia";
18             var matchQuery1 = from name in names1Text
19                               let t = name.Split(',')
20                               where t[0] == nameMatch
21                               select name;
22             var matchQuery2 = from name in names2Text
23                               let t = name.Split(',')
24                               where t[0] == nameMatch
25                               select name;
26 
27             var temp = matchQuery1.Concat(matchQuery2).OrderBy(x => x);
28             OutputQueryResult(temp, $"Concat based on partial name match \"{nameMatch}\":");      
1         private static void OutputQueryResult(IEnumerable<string> querys, string title)
 2         {
 3             Console.WriteLine(Environment.NewLine + title);
 4             foreach (var query in querys)
 5             {
 6                 Console.WriteLine(query);
 7             }
 8 
 9             Console.WriteLine($"{querys.Count()} total names in list");
10         }      
[C#] Linq To Objects - 如何操作字元串
[C#] Linq To Objects - 如何操作字元串

九、如何從多個源中填充對象集合

  不要嘗試将記憶體中的資料或檔案系統中的資料與仍在資料庫中的資料相聯接。此種跨域聯接會生成未定義的結果,因為資料庫查詢和其他類型的源定義聯接運算的方式可能不同。另外,如果資料庫中的資料量足夠大,則存在此類運算引發記憶體不足異常的風險。若要将資料庫資料與記憶體中的資料相聯接,請首先對資料庫查詢調用 ToList 或 ToArray,然後對傳回的集合執行聯接。  

1             //每行 names.csv 包含姓氏,名字,和身份證号,以逗号分隔。例如,Omelchenko,Svetlana,111
 2             var names = File.ReadAllLines(@"names.csv");
 3             //每行 scores.csv 包括身份證号碼和四個測試評分,以逗号分隔。例如,111,97,92,81,60 
 4             var scores = File.ReadAllLines(@"scores.csv");
 5 
 6             //使用一個匿名的類型合并資料源。
 7             //【注意】動态建立一個 int 的考試成績成員清單。
 8             //跳過分割字元串中的第一項,因為它是學生的身份證,不是一個考試成績
 9             var students = from name in names
10                            let t = name.Split(',')
11                            from score in scores
12 
13                            let t2 = score.Split(',')
14                            where t[2] == t2[0]
15                            select new
16                            {
17                                FirstName = t[0],
18                                LastName = t[1],
19                                ID = Convert.ToInt32(t[2]),
20                                ExamScores = (from scoreAsText in t2.Skip(1)
21                                              select Convert.ToInt32(scoreAsText)).ToList()
22                            };
23 
24             foreach (var student in students)
25             {
26                 Console.WriteLine(
27                     $"The average score of {student.FirstName} {student.LastName} is {student.ExamScores.Average()}.");
28             }      
[C#] Linq To Objects - 如何操作字元串

十、如何使用 group 将一個檔案拆分成多個檔案

  此示例示範一種進行以下操作的方法:合并兩個檔案的内容,然後建立一組以新方式組織資料的新檔案。 

[C#] Linq To Objects - 如何操作字元串
[C#] Linq To Objects - 如何操作字元串
Bankov, Peter
Holm, Michael
Garcia, Hugo
Potra, Cristina
Noriega, Fabricio
Aw, Kam Foo
Beebe, Ann
Toyoshima, Tim
Guy, Wey Yuan
Garcia, Debra      
[C#] Linq To Objects - 如何操作字元串
[C#] Linq To Objects - 如何操作字元串
Liu, Jinghao
Bankov, Peter
Holm, Michael
Garcia, Hugo
Beebe, Ann
Gilchrist, Beth
Myrcha, Jacek
Giakoumakis, Leo
McLin, Nkenge
El Yassir, Mehdi      
1             var fileA = File.ReadAllLines(@"names1.txt");
 2             var fileB = File.ReadAllLines(@"names2.txt");
 3 
 4             //并集:連接配接并删除重複的名字
 5             var mergeQuery = fileA.Union(fileB);
 6             //根據姓氏的首字母對姓名進行分組
 7             var query = from name in mergeQuery
 8                         let t = name.Split(',')
 9                         group name by t[0][0] into g
10                         orderby g.Key
11                         select g;
12 
13             //注意嵌套的 foreach 循環
14             foreach (var g in query)
15             {
16                 var fileName = @"testFile_" + g.Key + ".txt";
17                 Console.WriteLine(g.Key + ":");
18 
19                 //寫入檔案
20                 using (var sw = new StreamWriter(fileName))
21                 {
22                     foreach (var name in g)
23                     {
24                         sw.WriteLine(name);
25                         Console.WriteLine(" " + name);
26                     }
27                 }
28             }      
[C#] Linq To Objects - 如何操作字元串

  對于與資料檔案位于同一檔案夾中的每個組,程式将為這些組編寫單獨的檔案。

十一、如何向不同的檔案中加入内容

  此示例示範如何聯接兩個逗号分隔檔案中的資料,這兩個檔案共享一個用作比對鍵的共同值。如果您必須将兩個電子表格的資料或一個電子表格和一個其他格式的檔案的資料組合為一個新檔案,則此技術很有用。還可以修改此示例以适合任意種類的結構化文本。   

[C#] Linq To Objects - 如何操作字元串
[C#] Linq To Objects - 如何操作字元串
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      
[C#] Linq To Objects - 如何操作字元串
[C#] Linq To Objects - 如何操作字元串
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      

names.csv

  scores.csv:此檔案表示電子表格資料。第 1 列是學生的 ID,第 2 至 5 列是測驗分數。

  names.csv:此檔案表示一個電子表格。該電子表格包含學生的姓氏、名字和學生 ID。  

1             var names = File.ReadAllLines(@"names.csv");
 2             var scores = File.ReadAllLines(@"scores.csv");
 3 
 4             //Name:    Last[0],       First[1],  ID[2]
 5             //          Omelchenko,    Svetlana,  11
 6             //Score:   StudentID[0],  Exam1[1]   Exam2[2],  Exam3[3],  Exam4[4]
 7             //          111,           97,        92,        81,        60
 8 
 9             //該查詢基于 id 連接配接兩個不同的電子表格
10             var query = from name in names
11                         let t1 = name.Split(',')
12                         from score in scores
13                         let t2 = score.Split(',')
14                         where t1[2] == t2[0]
15                         orderby t1[0]
16                         select $"{t1[0]},{t2[1]},{t2[2]},{t2[3]},{t2[4]}";
17 
18             //輸出
19             OutputQueryResult(query, "Merge two spreadsheets:");      
1         private static void OutputQueryResult(IEnumerable<string> querys, string title)
 2         {
 3             Console.WriteLine(Environment.NewLine + title);
 4             foreach (var query in querys)
 5             {
 6                 Console.WriteLine(query);
 7             }
 8 
 9             Console.WriteLine($"{querys.Count()} total names in list");
10         }      
[C#] Linq To Objects - 如何操作字元串

十二、如何計算一個 CSV 文本檔案中的列值

  此示例示範如何對 .csv 檔案的列執行諸如 Sum、Average、Min 和 Max 等聚合計算。此處所示的示例原則可以應用于其他類型的結構化文本。   

[C#] Linq To Objects - 如何操作字元串
[C#] Linq To Objects - 如何操作字元串
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:假定第一清單示學員 ID,後面幾清單示四次考試的分數。  

1             var scores = File.ReadAllLines(@"scores.csv");
 2 
 3             //指定要計算的列
 4             const int examNum = 3;
 5 
 6             //scores.csv 格式:
 7             //Student ID    Exam#1  Exam#2  Exam#3  Exam#4
 8             //111,          97,     92,     81,     60
 9 
10             //+1 表示跳過第一列
11             //計算但一列
12             SingleColumn(scores, examNum+1);
13 
14             Console.WriteLine();
15 
16             //計算多列
17             MultiColumns(scores);      
1         private static void SingleColumn(IEnumerable<string> strs, int examNum)
 2         {
 3             Console.WriteLine("Single Column Query:");
 4 
 5             //查詢分兩步:
 6             // 1.分割字元串
 7             // 2.對要計算的列的值轉換為 int
 8             var query = from str in strs
 9                         let t = str.Split(',')
10                         select Convert.ToInt32(t[examNum]);
11 
12             //對指定的列進行統計
13             var average = query.Average();
14             var max = query.Max();
15             var min = query.Min();
16 
17             Console.WriteLine($"Exam #{examNum}: Average:{average:##.##} High Score:{max} Low Score:{min}");
18         }
19 
20         private static void MultiColumns(IEnumerable<string> strs)
21         {
22             Console.WriteLine("Multi Column Query:");
23 
24             //查詢步驟:
25             // 1.分割字元串
26             // 2.跳過 id 列(第一列)
27             // 3.将目前行的每個評分都轉換成 int,并選擇整個序列作為一行結果。 
28             var query = from str in strs
29                         let t1 = str.Split(',')
30                         let t2 = t1.Skip(1)
31                         select (from t in t2
32                                 select Convert.ToInt32(t));
33 
34             //執行查詢并緩存結果以提高性能
35             var results = query.ToList();
36             //找出結果的列數
37             var count = results[0].Count();
38 
39             //執行統計
40             //為每一列分數的循環執行一次循環
41             for (var i = 0; i < count; i++)
42             {
43                 var query2 = from result in results
44                              select result.ElementAt(i);
45 
46                 var average = query2.Average();
47                 var max = query2.Max();
48                 var min = query2.Min();
49 
50                 //+1 因為 #1 表示第一次考試
51                 Console.WriteLine($"Exam #{i + 1} Average: {average:##.##} High Score: {max} Low Score: {min}");
52             }
53 
54         }      
[C#] Linq To Objects - 如何操作字元串

  查詢的工作原理是使用 Split 方法将每一行文本轉換為數組。每個數組元素表示一列。最後,每一列中的文本都轉換為其數字表示形式。如果檔案是制表符分隔檔案,隻需将 Split 方法中的參數更新為 \t。  

================================================== 傳送門分割線 ================================================== 

LINQ 其它随筆 -  《開始使用 LINQ》

         《LINQ:進階 - LINQ 标準查詢操作概述》

         《Linq To Objects - 如何操作檔案目錄》

【首聯】http://www.cnblogs.com/liqingwen/p/5814204.html

【參考】https://msdn.microsoft.com/zh-cn/library/bb397915(v=vs.100).aspx

繼續閱讀