C# LINQ學習筆記三:LINQ to OBJECT之操做字符串

    本筆記摘抄自:http://www.javashuo.com/article/p-gagbblxc-o.html,記錄一下學習過程以備後續查用。html

    1、統計單詞在字符串中出現的次數node

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

IndexOf方法。正則表達式

class Program { static void Main(string[] args) { #region LINQ 統計單詞在字符串中出現的次數
            const 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."; const string searchWord = "data"; //字符串轉換成數組
            var source = text.Split(new[] { '.', '?', '!', ' ', ';', ':', ',' }, StringSplitOptions.RemoveEmptyEntries); //建立查詢,並忽略大小寫比較。
            var query = from word in source where string.Equals(word, searchWord, StringComparison.InvariantCultureIgnoreCase) select word; //統計匹配數量
            var wordCount = query.Count(); Console.WriteLine($"{wordCount} occurrences(s) of the search word \"{searchWord}\" were found."); Console.Read(); #endregion } }
View Code

    運行結果以下:數據庫

    2、查詢包含指定一組單詞的句子express

    此示例演示如何查找文本文件中包含指定一組單詞中每一個單詞匹配項的句子。雖然在此示例中搜索條件數組是硬編碼的,但也能夠在運行時動態填充此npm

數組。跨域

class Program { static void Main(string[] args) { #region LINQ 查詢包含指定一組單詞的句子
            const 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."; //將文本塊切割成數組
            var sentences = text.Split('.', '?', '!'); //定義搜索條件,此列表能夠在運行時動態添加。
            string[] wordsToMatch = { "Historically", "data", "integrated" }; var query = from sentence in sentences let t = sentence.Split(new char[] { '.', '?', '!', ' ', ';', ':', ',' }, StringSplitOptions.RemoveEmptyEntries) where t.Distinct().Intersect(wordsToMatch).Count() == wordsToMatch.Length   //去重,取交集後的數量對比。
                        select sentence; foreach (var sentence in query) { Console.WriteLine(sentence); } Console.Read(); #endregion } }
View Code

    運行結果以下:數組

    查詢運行時首先將文本拆分紅句子,而後將句子拆分紅包含每一個單詞的字符串數組。對於每一個這樣的數組,Distinct<TSource> 方法移除全部重複的單詞,緩存

而後查詢對單詞數組和wordstoMatch數組執行Intersect<TSource>操做。若是交集的計數與wordsToMatch數組的計數相同,則在單詞中找到了全部的單詞,

而後返回原始句子。

    在對Split的調用中,使用標點符號做爲分隔符,以從字符串中移除標點符號。若是您沒有這樣作,則假如您有一個字符串「Historically,」,該字符串不會

與wordsToMatch數組中的「Historically」相匹配。根據源文本中標點的類型,您可能必須使用其餘分隔符。

    3、在字符串中查詢字符

    由於String類實現泛型IEnumerable<T>接口,因此能夠將任何字符串做爲字符序列進行查詢。可是,這不是LINQ的常見用法。若要執行復雜的模式匹配操

做,請使用Regex類。

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

class Program { static void Main(string[] args) { #region LINQ 在字符串中查詢字符
            const string source = "ABCDE99F-J74-12-89A"; //只選擇數字的字符 
            var digits = from character in source where char.IsDigit(character) select character; Console.Write("Digit:"); foreach (var digit in digits) { Console.Write($"{digit} "); } Console.WriteLine(); //選擇第一個"-"以前的全部字符
            var query = source.TakeWhile(x => x != '-'); foreach (var character in query) { Console.Write(character); } Console.Read(); #endregion } }
View Code

    運行結果以下:

    4、正則表達式結合LINQ查詢

    此示例演示如何使用Regex類建立正則表達式以便在文本字符串中進行更復雜的匹配。使用LINQ查詢能夠方便地對您要用正則表達式搜索的文件進行準確

篩選以及對結果進行加工。

class Program { static void Main(string[] args) { #region LINQ 正則表達式結合LINQ查詢
            //請根據不一樣版本的VS進行路徑修改
            const string floder = @"C:\Program Files (x86)\Microsoft Visual Studio\"; var fileInfoes = GetFiles(floder); //建立正則表達式來尋找全部的"Visual"
            var searchTerm = new Regex(@"http://(www.w3.org|www.npmjs.org)"); //搜索每個「.html」文件 //經過where找到匹配項 //注意:select中的變量要求顯示聲明其類型,由於MatchCollection不是泛型IEnumerable集合。
            var query = from fileInfo in fileInfoes where fileInfo.Extension == ".html" let text = File.ReadAllText(fileInfo.FullName) let matches = searchTerm.Matches(text) where matches.Count > 0
                        select new { name = fileInfo.FullName, matchValue = from Match match in matches select match.Value }; Console.WriteLine($"The term \"{searchTerm}\" was found in:"); Console.WriteLine(); foreach (var q in query) { //修剪匹配找到的文件中的路徑
                Console.WriteLine($"name==>{q.name.Substring(floder.Length - 1)}"); //輸出找到的匹配值
                foreach (var v in q.matchValue) { Console.WriteLine($"matchValue==>{v}"); } //輸出空白行
 Console.WriteLine(); } Console.Read(); #endregion } /// <summary>
        /// 獲取指定路徑的文件信息 /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private static IList<FileInfo> GetFiles(string path) { var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories); return files.Select(file => new FileInfo(file)).ToList(); } }
View Code

    運行結果以下:

    5、查找兩個集合間的差別

    此示例演示如何使用LINQ對兩個字符串列表進行比較,並輸出那些位於text1.txt中但不在text2.txt中的行。

Bankov, Peter Holm, Michael Garcia, Hugo Potra, Cristina Noriega, Fabricio Aw, Kam Foo Beebe, Ann Toyoshima, Tim Guy, Wey Yuan Garcia, Debra
text1.txt
Liu, Jinghao Bankov, Peter Holm, Michael Garcia, Hugo Beebe, Ann Gilchrist, Beth Myrcha, Jacek Giakoumakis, Leo McLin, Nkenge El Yassir, Mehdi
text2.txt
class Program { static void Main(string[] args) { #region LINQ 查找兩個集合間的差別
            //建立數據源
            var text1 = File.ReadAllLines(@"..\..\text1.txt"); var text2 = File.ReadAllLines(@"..\..\text2.txt"); //建立查詢,這裏必須使用方法語法。
            var query = text1.Except(text2); //執行查詢
            Console.WriteLine("The following lines are in text1.txt but not text2.txt"); foreach (var name in query) { Console.WriteLine(name); } Console.Read(); #endregion } }
View Code

    運行結果以下:

    注:某些類型的查詢操做(如 Except<TSource>Distinct<TSource>Union<TSource> 和 Concat<TSource>)只能用基於方法的語法表示。

    6、排序或過濾任意單詞或字段的文本數據

    下面的示例演示如何按結構化文本(如逗號分隔值)行中的任意字段對該文本行進行排序,可在運行時動態指定該字段。

    假定scores.csv中的字段表示學生的ID號,後面跟着四個測驗分數。

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
class Program { static void Main(string[] args) { #region LINQ 排序或過濾任意單詞或字段的文本數據
            //建立數據源
            var scores = File.ReadAllLines(@"..\..\scores.csv"); //能夠改成0~4的任意值
            const int sortIndex = 1; //演示從方法返回查詢變量,非查詢結果。
            foreach (var score in SplitSortQuery(scores, sortIndex)) { Console.WriteLine(score); } Console.Read(); #endregion } /// <summary>
        /// 分割字符串排序 /// </summary>
        /// <param name="scores"></param>
        /// <param name="num"></param>
        /// <returns></returns>
        private static IEnumerable<string> SplitSortQuery(IEnumerable<string> scores, int num) { var query = from line in scores let fields = line.Split(',') orderby fields[num] descending select line; return query; } }
View Code

    運行結果以下:

    7、對一個分割的文件的字段從新排序

    逗號分隔值 (CSV) 文件是一種文本文件,一般用於存儲電子表格數據或其餘由行和列表示的表格數據。經過使用Split方法分隔字段,能夠很是輕鬆地使用

LINQ來查詢和操做CSV文件。事實上,可使用此技術來從新排列任何結構化文本行部分。此技術不侷限於CSV文件。

    在下面的示例中,假定有三列分別表明學生的「姓氏」、「名字」和「ID」,這些字段基於學生的姓氏按字母順序排列。查詢生成一個新序列,其中首先出現的是

ID列,後面的第二列組合了學生的名字和姓氏。根據ID字段從新排列各行,結果保存到新文件,但不修改原始數據。

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
spread.csv
class Program { static void Main(string[] args) { #region LINQ 對一個分割的文件的字段從新排序
            //數據源
            var lines = File.ReadAllLines(@"..\..\spread.csv"); //將舊數據的第2列的字段放到第一位,逆向結合第0列和第1列的字段。
            var query = from line in lines let t = line.Split(',') orderby t[2] select $"{t[2]} {t[1]} {t[0]}"; foreach (var item in query) { Console.WriteLine(item); } Console.Read(); #endregion } }
View Code

    運行結果以下:

    8、組合和比較字符串集合

    此示例演示如何合併包含文本行的文件,而後排序結果。具體來講,此示例演示如何對兩組文本行執行簡單的串聯、聯合和交集。

    注:text1.txt及text2.txt與5、的一致。

class Program { static void Main(string[] args) { #region LINQ 組合和比較字符串集合
            var text1 = File.ReadAllLines(@"..\..\text1.txt"); var text2 = File.ReadAllLines(@"..\..\text2.txt"); //簡單鏈接並排序,重複保存。 
            var concatQuery = text1.Concat(text2).OrderBy(x => x); OutputQueryResult(concatQuery, "Simple concatenate and sort,duplicates are preserved:"); //基於默認字符串比較器鏈接,並刪除重名。 
            var unionQuery = text1.Union(text2).OrderBy(x => x); OutputQueryResult(unionQuery, "Union removes duplicate names:"); //查找在兩個文件中出現的名稱 
            var intersectQuery = text1.Intersect(text2).OrderBy(x => x); OutputQueryResult(intersectQuery, "Merge based on intersect:"); //在每一個列表中找到匹配的字段,使用concat將兩個結果合併,而後使用默認的字符串比較器進行排序。
            const string nameMatch = "Garcia"; var matchQuery1 = from name in text1 let t = name.Split(',') where t[0] == nameMatch select name; var matchQuery2 = from name in text2 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}\":"); Console.Read(); #endregion } /// <summary>
        /// 輸出查詢結果 /// </summary>
        /// <param name="querys"></param>
        /// <param name="title"></param>
        private static void OutputQueryResult(IEnumerable<string> querys, string title) { Console.WriteLine(Environment.NewLine + title); foreach (var query in querys) { Console.WriteLine(query); } Console.WriteLine($"Total {querys.Count()} names in list."); } }
View Code

    運行結果以下:

    9、從多個源中填充對象集合

    不要嘗試將內存中的數據或文件系統中的數據與仍在數據庫中的數據相聯接。此種跨域聯接會生成未定義的結果,由於數據庫查詢和其餘類型的源定義聯

接運算的方式可能不一樣。另外,若是數據庫中的數據量足夠大,則存在此類運算引起內存不足異常的風險。若要將數據庫數據與內存中的數據相聯接,請首

先對數據庫查詢調用ToList或ToArray,而後對返回的集合執行聯接。

class Program { static void Main(string[] args) { #region LINQ 從多個源中填充對象集合
            //spread.csv每行包含姓氏、名字和身份證號,以逗號分隔。例如,Omelchenko,Svetlana,111
            var names = File.ReadAllLines(@"..\..\spread.csv"); //scores.csv每行包括身份證號碼和四個測試評分,以逗號分隔。例如,111,97,92,81,60 
            var scores = File.ReadAllLines(@"..\..\scores.csv"); //使用一個匿名的類型合併數據源。 //注:動態建立一個int的考試成績成員列表。 //跳過度割字符串中的第一項,由於它是學生的身份證,不是一個考試成績。
            var students = from name in names let t1 = name.Split(',') from score in scores let t2 = score.Split(',') where t1[2] == t2[0] select new { FirstName = t1[0], LastName = t1[1], ID = Convert.ToInt32(t1[2]), ExamScores = (from score in t2.Skip(1) select Convert.ToInt32(score)).ToList() }; foreach (var student in students) { Console.WriteLine($"The average score of {student.FirstName} {student.LastName} is {student.ExamScores.Average()}."); } Console.Read(); #endregion } }
View Code

    運行結果以下:

    10、使用group將一個文件拆分紅多個文件

    此示例演示一種進行如下操做的方法:合併兩個文件的內容,而後建立一組以新方式組織數據的新文件。

    注:text1.txt及text2.txt與5、的一致。

class Program { static void Main(string[] args) { #region LINQ 使用group將一個文件拆分紅多個文件
            var text1 = File.ReadAllLines(@"..\..\text1.txt"); var text2 = File.ReadAllLines(@"..\..\text2.txt"); //並集:鏈接並刪除重複的名字
            var mergeQuery = text1.Union(text2); //根據姓氏的首字母對姓名進行分組
            var query = from name in mergeQuery let t = name.Split(',') group name by t[0][0] into g orderby g.Key select g; //注意嵌套的 foreach 循環
            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); } } } Console.Read(); #endregion } }
View Code

    運行結果以下:

    11、向不一樣的文件中加入內容

    此示例演示如何聯接兩個逗號分隔文件中的數據,這兩個文件共享一個用做匹配鍵的共同值。若是您必須將兩個電子表格的數據或一個電子表格和一個其

他格式的文件的數據組合爲一個新文件,則此技術頗有用,還能夠修改此示例以適合任意種類的結構化文本。

class Program { static void Main(string[] args) { #region LINQ 向不一樣的文件中加入內容
            var names = File.ReadAllLines(@"..\..\spread.csv"); var scores = File.ReadAllLines(@"..\..\scores.csv"); //該查詢基於ID鏈接兩個不一樣的電子表格
            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:"); Console.Read(); #endregion } /// <summary>
        /// 輸出查詢結果 /// </summary>
        /// <param name="querys"></param>
        /// <param name="title"></param>
        private static void OutputQueryResult(IEnumerable<string> querys, string title) { Console.WriteLine(Environment.NewLine + title); foreach (var query in querys) { Console.WriteLine(query); } Console.WriteLine($"Total {querys.Count()} names in list."); } }
View Code

    運行結果以下:

    12、計算一個CSV文本文件中的列值

    此示例演示如何對.csv文件的列執行諸如Sum、Average、Min和Max等聚合計算,此示例能夠應用於其餘類型的結構化文本。

class Program { static void Main(string[] args) { #region LINQ 計算一個CSV文本文件中的列值
            var scores = File.ReadAllLines(@"..\..\scores.csv"); //指定要計算的列
            const int examNum = 3; //+1表示跳過第一列 //統計單列
            SingleColumn(scores, examNum + 1); Console.WriteLine(); //統計多列
 MultiColumns(scores); Console.Read(); #endregion } /// <summary>
        /// 統計單列 /// </summary>
        /// <param name="lines"></param>
        /// <param name="examNum"></param>
        private static void SingleColumn(IEnumerable<string> lines, int examNum) { Console.WriteLine("Single Column Query:"); //查詢步驟: //1.分割字符串 //2.對要計算的列的值轉換爲int
            var query = from line in lines let t = line.Split(',') select Convert.ToInt32(t[examNum]); //對指定的列進行統計
            var average = query.Average(); var max = query.Max(); var min = query.Min(); Console.WriteLine($"Exam #{examNum}: Average:{average:##.##} High Score:{max} Low Score:{min}"); } /// <summary>
        /// 統計多列 /// </summary>
        /// <param name="lines"></param>
        private static void MultiColumns(IEnumerable<string> lines) { Console.WriteLine("Multi Column Query:"); //查詢步驟: //1.分割字符串 //2.跳過ID列(第一列) //3.將當前行的每一個評分都轉換成int,並選擇整個序列做爲一行結果。 
            var query1 = from line in lines let t1 = line.Split(',') let t2 = t1.Skip(1) select (from t in t2 select Convert.ToInt32(t)); //執行查詢並緩存結果以提升性能
            var results = query1.ToList(); //找出結果的列數
            var count = results[0].Count(); //執行統計
            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表示第一次考試
                Console.WriteLine($"Exam #{i + 1} Average: {average:##.##} High Score: {max} Low Score: {min}"); } } }
View Code

    運行結果以下:

相關文章
相關標籤/搜索