下面的示例演示如何按结构化文本(如逗号分隔值)行中的任意字段对该文本行进行排序。可在运行时动态指定该字段。假定 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
1 //创建数据源
2 var scores = File.ReadAllLines(@"scores.csv");
3 //可以改为 0~4 的任意值
4 const int sortField = 1;
6 //演示从方法返回查询
7 //返回查询变量,非查询结果
8 //这里执行查询
9 foreach (var score in RunQuery(scores, sortField))
10 {
11 Console.WriteLine(score);
1 private static IEnumerable<string> RunQuery(IEnumerable<string> score, int num)
3 //分割字符串来排序
4 var query = from line in score
5 let fields = line.Split(',')
6 orderby fields[num] descending
7 select line;
9 return query;
逗号分隔值 (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
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]}";
9 foreach (var q in query)
10 {
11 Console.WriteLine(q);
12 }
14 //写入文件
15 File.WriteAllLines("spreadsheet2.csv", query);
此示例演示如何合并包含文本行的文件,然后排序结果。具体来说,此示例演示如何对两组文本行执行简单的串联、联合和交集。
Bankov, Peter
Holm, Michael
Garcia, Hugo
Potra, Cristina
Noriega, Fabricio
Aw, Kam Foo
Beebe, Ann
Toyoshima, Tim
Guy, Wey Yuan
Garcia, Debra
names1.txt
Liu, Jinghao
Bankov, Peter
Holm, Michael
Garcia, Hugo
Beebe, Ann
Gilchrist, Beth
Myrcha, Jacek
Giakoumakis, Leo
McLin, Nkenge
El Yassir, Mehdi
names2.txt
1 var names1Text = File.ReadAllLines(@"names1.txt");
2 var names2Text = File.ReadAllLines(@"names2.txt");
4 //简单连接,并排序。重复保存。
5 var concatQuery = names1Text.Concat(names2Text).OrderBy(x => x);
6 OutputQueryResult(concatQuery, "Simple concatenate and sort. Duplicates are preserved:");
8 //基于默认字符串比较器连接,并删除重名。
9 var unionQuery = names1Text.Union(names2Text).OrderBy(x => x);
10 OutputQueryResult(unionQuery, "Union removes duplicate names:");
12 //查找在两个文件中出现的名称
13 var intersectQuery = names1Text.Intersect(names2Text).OrderBy(x => x);
14 OutputQueryResult(intersectQuery, "Merge based on intersect:");
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;
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)
3 Console.WriteLine(Environment.NewLine + title);
4 foreach (var query in querys)
6 Console.WriteLine(query);
9 Console.WriteLine($"{querys.Count()} total names in list");
不要尝试将内存中的数据或文件系统中的数据与仍在数据库中的数据相联接。此种跨域联接会生成未定义的结果,因为数据库查询和其他类型的源定义联接运算的方式可能不同。另外,如果数据库中的数据量足够大,则存在此类运算引发内存不足异常的风险。若要将数据库数据与内存中的数据相联接,请首先对数据库查询调用 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");
6 //使用一个匿名的类型合并数据源。
7 //【注意】动态创建一个 int 的考试成绩成员列表。
8 //跳过分割字符串中的第一项,因为它是学生的身份证,不是一个考试成绩
9 var students = from name in names
10 let t = name.Split(',')
11 from score in scores
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 };
24 foreach (var student in students)
25 {
26 Console.WriteLine(
27 $"The average score of {student.FirstName} {student.LastName} is {student.ExamScores.Average()}.");
1 var fileA = File.ReadAllLines(@"names1.txt");
2 var fileB = File.ReadAllLines(@"names2.txt");
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;
13 //注意嵌套的 foreach 循环
14 foreach (var g in query)
15 {
16 var fileName = @"testFile_" + g.Key + ".txt";
17 Console.WriteLine(g.Key + ":");
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 }
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");
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
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]}";
18 //输出
19 OutputQueryResult(query, "Merge two spreadsheets:");
1 private static void OutputQueryResult(IEnumerable<string> querys, string title)
3 Console.WriteLine(Environment.NewLine + title);
4 foreach (var query in querys)
6 Console.WriteLine(query);
9 Console.WriteLine($"{querys.Count()} total names in list");
scores.csv:假定第一列表示学员 ID,后面几列表示四次考试的分数。
1 var scores = File.ReadAllLines(@"scores.csv");
3 //指定要计算的列
4 const int examNum = 3;
6 //scores.csv 格式:
7 //Student ID Exam#1 Exam#2 Exam#3 Exam#4
8 //111, 97, 92, 81, 60
10 //+1 表示跳过第一列
11 //计算但一列
12 SingleColumn(scores, examNum+1);
14 Console.WriteLine();
16 //计算多列
17 MultiColumns(scores);
1 private static void SingleColumn(IEnumerable<string> strs, int examNum)
3 Console.WriteLine("Single Column Query:");
5 //查询分两步:
6 // 1.分割字符串
7 // 2.对要计算的列的值转换为 int
8 var query = from str in strs
9 let t = str.Split(',')
10 select Convert.ToInt32(t[examNum]);
12 //对指定的列进行统计
13 var average = query.Average();
14 var max = query.Max();
15 var min = query.Min();
17 Console.WriteLine($"Exam #{examNum}: Average:{average:##.##} High Score:{max} Low Score:{min}");
18 }
20 private static void MultiColumns(IEnumerable<string> strs)
21 {
22 Console.WriteLine("Multi Column Query:");
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));
34 //执行查询并缓存结果以提高性能
35 var results = query.ToList();
36 //找出结果的列数
37 var count = results[0].Count();
39 //执行统计
40 //为每一列分数的循环执行一次循环
41 for (var i = 0; i < count; i++)
42 {
43 var query2 = from result in results
44 select result.ElementAt(i);
46 var average = query2.Average();
47 var max = query2.Max();
48 var min = query2.Min();
50 //+1 因为 #1 表示第一次考试
51 Console.WriteLine($"Exam #{i + 1} Average: {average:##.##} High Score: {max} Low Score: {min}");
52 }
================================================== 传送门分割线 ==================================================
《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
LINQ 其它随笔 - 《开始使用 LINQ》