Linq To Objects - 如何操作文件目录

  开篇语:  

  上次发布的 《LINQ:进阶 - LINQ 标准查询操作概述》 社会反响不错,但自己却始终觉得缺点什么!“纸上得来终觉浅,绝知此事要躬行”,没错,就是实战!这次让我们一起来看看一些操作文件目录的技巧,也许能引我们从不同的角度思考问题,从而走出思维的死角!

  这是与 《Linq To Objects - 如何操作字符串》 风格相似的操作技巧篇。

  这里主要贴的是代码,所以会很枯燥乏味,要知道,放弃远远比一章章的看下去要简单得多。博主猜测:看的每一小点都可能令你有“柳暗花明又一村”之意!

  许多文件系统操作实质上是查询,因此非常适合使用 LINQ 方法。
  【注意】本节中的查询是非破坏性查询。它们不用于更改原始文件或文件夹的内容。这遵循了查询不应引起任何副作用这条规则。通常,修改源数据的任何代码(包括执行创建/更新/删除运算符的查询)应与只查询数据的代码分开。
 

                    --演示如何通过检查文件的 FileInfo 对象的一个或多个属性来搜索文件。

  (2)如何按照扩展名对文件进行分组?

                    --演示如何根据文件扩展名返回 FileInfo 对象组。

  (3)如何查询一组文件夹中的总字节数?

                    --演示如何返回指定目录树中的所有文件中的总字节数。

  (4)如何比较两个文件夹中的内容?

                    --演示如何返回位于两个指定文件夹中的所有文件,以及仅位于其中一个文件夹中的所有文件。
                    --演示如何返回目录树中的最大文件、最小文件或指定数量的文件。

                    --演示如何对出现在指定目录树的多个位置的所有文件名进行分组。此外,还演示如何根据自定义比较器执行更复杂的比较。

                    --演示如何循环访问树中的文件夹,打开每个文件以及查询文件的内容。
 

一、如何查询具有指定属性或名称的文件

  此示例演示如何查找指定目录树中具有指定文件扩展名(例如“.txt”)的所有文件,还演示如何根据创建时间返回树中最新或最旧的文件。

             //该查询将所有生产的完整路径。txt文件指定的文件夹包括子文件夹下。
const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 14.0\";
//取文件系统快照
var dir = new DirectoryInfo(path);
//该方法假定应用程序在指定路径下的所有文件夹都具有搜索权限。
var files = dir.GetFiles("*.*", SearchOption.AllDirectories); //创建查询
var fileQuery = from file in files
where file.Extension == ".html"
orderby file.Name
select file; //执行查询
foreach (var file in fileQuery)
{
Console.WriteLine(file.FullName);
} //创建和执行一个新的查询,通过查询旧文件的创建时间作为一个出发点
//Last:选最后一个,因为是按日期升序,所以最新的是指最后一个
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}");

  

  图中执行结果可能与您的不一样。

二、如何按照扩展名对文件进行分组

  此示例演示如何使用 LINQ 对文件或文件夹列表执行高级分组和排序操作。此外,它还演示如何使用 Skip<TSource> 和 Take<TSource> 方法对控制台窗口中的输出进行分页。  
  下面的查询演示如何按文件扩展名对指定目录树的内容进行分组。
             const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7";
//“path”的长度,后续用于在输出时去掉“path”这段前缀
var trimLength = path.Length;
//取文件系统快照
var dir = new DirectoryInfo(path);
//该方法假定应用程序在指定路径下的所有文件夹都具有搜索权限。
var files = dir.GetFiles("*.*", SearchOption.AllDirectories); //创建查询
var query = from file in files
group file by file.Extension.ToLower() into fileGroup
orderby fileGroup.Key
select fileGroup; //一次显示一组。如果列表实体的行数大于控制台窗口中的行数,则分页输出。
PageOutput(trimLength, query);
         private static void PageOutput(int rootLength, IOrderedEnumerable<IGrouping<string, FileInfo>> query)
{
//跳出分页循环的标志
var isAgain = true;
//控制台输出的高度
var numLines = Console.WindowHeight - ; //遍历分组集合
foreach (var g in query)
{
var currentLine = ; do
{
Console.Clear();
Console.WriteLine(string.IsNullOrEmpty(g.Key) ? "[None]" : g.Key); //从“currentLine”开始显示“numLines”条数
var resultPage = g.Skip(currentLine).Take(numLines); //执行查询
foreach (var info in resultPage)
{
Console.WriteLine("\t{0}", info.FullName.Substring(rootLength));
} //记录输出行数
currentLine += numLines;
Console.WriteLine("点击“任意键”继续,按“End”键退出"); //给用户选择是否跳出
var key = Console.ReadKey().Key;
if (key != ConsoleKey.End) continue; isAgain = false;
break;
} while (currentLine < g.Count()); if (!isAgain)
{
break;
}
} }

  

  图中执行结果可能与您的不一样。

  此程序的输出可能会很长,具体取决于本地文件系统的细节以及 path 的设置。为了使您可以查看所有结果,此示例还演示如何按页查看结果。这些方法可应用于 Windows 和 Web 应用程序。请注意,由于代码将对组中的项进行分页,因此需要嵌套的 foreach 循环。此外,还会使用某他某个逻辑来计算列表中的当前位置,以及使用户可以停止分页并退出程序。在这种特定情况下,将针对原始查询的缓存结果运行分页查询。

三、如何查询一组文件夹中的总字节数

  此示例演示如何检索指定文件夹及其所有子文件夹中的所有文件所使用的总字节数。
  Sum 方法添加在 select 子句中选择的所有项的值。您可以轻松修改此查询以检索指定目录树中的最大或最小文件,方法是调用 Min<TSource> 或 Max<TSource> 方法,而不是 Sum
             const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC#";
var dir = new DirectoryInfo(path);
var files = dir.GetFiles("*.*", SearchOption.AllDirectories); var query = from file in files
select file.Length; //缓存结果,以避免多次访问文件系统
var fileLengths = query as long[] ?? query.ToArray();
//返回最大文件的大小
var largestLength = fileLengths.Max();
//返回指定文件夹下的所有文件中的总字节数
var totalBytes = fileLengths.Sum();
Console.WriteLine(); Console.WriteLine("There are {0} bytes in {1} files under {2}",
totalBytes, files.Count(), path);
Console.WriteLine("The largest files is {0} bytes.", largestLength);

  

  图中执行结果可能与您的不一样。

  如果您只需要统计特定目录树中的字节数,则可以更高效地实现此目的,而无需创建 LINQ 查询,因为该查询会引发创建列表集合作为数据源的系统开销。随着查询复杂度的增加,或者当您必须对同一数据源运行多个查询时,LINQ 方法的有用性也会随之增加。

四、如何比较两个文件夹中的内容

  此示例演示比较两个文件列表的三种方法:

    (1)查询一个指定两个文件列表是否相同的布尔值;

    (2)查询用于检索同时位于两个文件夹中的文件的交集;

    (3)查询用于检索位于一个文件夹中但不在另一个文件夹中的文件的差集;

             //创建两个带比较的文件夹
const string path1 = @"E:\Test1";
const string path2 = @"E:\Test2"; var dir1 = new DirectoryInfo(path1);
var dir2 = new DirectoryInfo(path2); //取文件快照
var files1 = dir1.GetFiles("*.*", SearchOption.AllDirectories);
var files2 = dir2.GetFiles("*.*", SearchOption.AllDirectories); //自定义文件比较器
var comparer = new FileComparer(); //该查询确定两个文件夹包含相同的文件列表,基于自定义文件比较器。查询立即执行,因为它返回一个bool。
var areIdentical = files1.SequenceEqual(files2, comparer);
Console.WriteLine(areIdentical == true ? "the two folders are the same" : "The two folders are not the same"); //交集:找相同的文件
var queryCommonFiles = files1.Intersect(files2, comparer); var commonFiles = queryCommonFiles as FileInfo[] ?? queryCommonFiles.ToArray();
if (commonFiles.Any())
{
Console.WriteLine("The following files are in both folders:");
foreach (var v in commonFiles)
{
Console.WriteLine(v.FullName);
}
}
else
{
Console.WriteLine("There are no common files in the two folders.");
} //差集:对比两个文件夹的差异
var diffQuery = files1.Except(files2, comparer); Console.WriteLine("The following files are in list1 but not list2:");
foreach (var v in diffQuery)
{
Console.WriteLine(v.FullName);
}
         //该实现定义了一个非常简单的两个 FileInfo 对象之间的比较。它只比较文件的名称和它们字节数的长度
public class FileComparer : IEqualityComparer<FileInfo>
{
public bool Equals(FileInfo x, FileInfo y)
{
return string.Equals(x.Name, y.Name, StringComparison.CurrentCultureIgnoreCase) && x.Length == y.Length;
} //返回一个比较标准的哈希值。根据 IEqualityComparer 规则,如果相等,那么哈希值也必须是相等的。
//因为这里所定义的相等只是一个简单的值相等,而不是引用标识,所以两个或多个对象将产生相同的哈希值是可能的。
public int GetHashCode(FileInfo obj)
{
var s = string.Format("{0}{1}", obj.Name, obj.Length); return s.GetHashCode();
}
}

  

  图中执行结果可能与您的不一样。

  【注意】 可以修改上述这些方法以便对任意类型的对象序列进行比较。
  此处显示的 FileComparer 类演示如何将自定义比较器类与标准查询运算符一起使用。该类不是为在实际方案中使用而设计的。它只是使用每个文件的名称和长度(以字节为单位)来确定每个文件夹的内容是否相同。在实际方案中,应对此比较器进行修改以执行更严格的相等性检查。
 

五、如何在目录树中查询最大的文件

  此示例演示与文件大小(以字节为单位)相关的五种查询:

    (1)如何检索最大文件的大小(以字节为单位);

    (2)如何检索最小文件的大小(以字节为单位);

    (3)如何从指定的根文件夹下的一个或多个文件夹检索 FileInfo 对象最大或最小文件;

    (4)如何检索一个序列,如 10 个最大文件。

  下面的示例包含五种不同的查询,这些查询演示如何根据文件大小(以字节为单位)查询和分组文件。可以轻松地修改这些示例,以使查询基于 FileInfo 对象的某个其他属性。  
             const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC#";
var dir = new DirectoryInfo(path);
var files = dir.GetFiles("*.*", SearchOption.AllDirectories); var query = from file in files
select file.Length; //返回最大文件的大小
var maxSize = query.Max();
Console.WriteLine("The length of the largest file under {0} is {1}",
path, maxSize); //倒序排列
var query2 = from file in files
let len = file.Length
where len >
orderby len descending
select file; var fileInfos = query2 as FileInfo[] ?? query2.ToArray();
//倒序排列的第一个就是最大的文件
var longestFile = fileInfos.First();
//倒序排列的第一个就是最小的文件
var smallestFile = fileInfos.Last(); Console.WriteLine("The largest file under {0} is {1} with a length of {2} bytes",
path, longestFile.FullName, longestFile.Length);
Console.WriteLine("The smallest file under {0} is {1} with a length of {2} bytes",
path, smallestFile.FullName, smallestFile.Length);
Console.WriteLine("===== The 10 largest files under {0} are: =====", path); //返回前10个最大的文件
var queryTenLargest = fileInfos.Take();
foreach (var v in queryTenLargest)
{
Console.WriteLine("{0}: {1} bytes", v.FullName, v.Length);
}
 
   
  图中执行结果可能与您的不一样。

  若要返回一个或多个完整的 FileInfo 对象,查询必须首先检查数据源中的每个对象,然后按这些对象的 Length 属性的值排序它们。然后查询可以返回具有最大长度的单个对象或序列。使用 First<TSource> 可返回列表中的第一个元素。使用 Take<TSource> 可返回前 n 个元素。指定降序排序顺序可将最小的元素放在列表的开头。

六、如何在目录树中查询重复的文件

     有时,多个文件夹中可能存在同名的文件。例如,在 Visual Studio 安装文件夹中,有多个文件夹包含 readme.htm 文件。此示例演示如何在指定的根文件夹中查询这样的重复文件名。第二个示例演示如何查询其大小和创建时间也匹配的文件。 
         static void Main(string[] args)
{
QueryDuplicates();
//QueryDuplicates2(); Console.ReadKey();
}
         static void QueryDuplicates()
{
const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 12.0";
var dir = new DirectoryInfo(path);
var files = dir.GetFiles("*.*", SearchOption.AllDirectories);
var charsToSkip = path.Length; var queryDupNames = (from file in files
group file.FullName.Substring(charsToSkip) by file.Name into fileGroup
where fileGroup.Count() >
select fileGroup).Distinct(); PageOutput<string, string>(queryDupNames);
}
         private static void QueryDuplicates2()
{
const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 12.0";
var dir = new DirectoryInfo(path);
var files = dir.GetFiles("*.*", SearchOption.AllDirectories);
//路径的长度
var charsToSkip = path.Length; //注意一个复合键的使用。三个属性都匹配的文件属于同一组。
//匿名类型也可以用于复合键,但不能跨越方法边界。
var queryDupFiles = from file in files
group file.FullName.Substring(charsToSkip) by
new PortableKey() { Name = file.Name, CreationTime = file.CreationTime, Length = file.Length }
into fileGroup
where fileGroup.Count() >
select fileGroup; var queryDupNames = queryDupFiles as IGrouping<PortableKey, string>[] ?? queryDupFiles.ToArray();
var list = queryDupNames.ToList();
var i = queryDupNames.Count(); //分页输出
PageOutput<PortableKey, string>(queryDupNames); }
         private static void PageOutput<TK, TV>(IEnumerable<IGrouping<TK, TV>> queryDupNames)
{
//跳出分页循环的标志
var isAgain = true;
var numLines = Console.WindowHeight - ; var dupNames = queryDupNames as IGrouping<TK, TV>[] ?? queryDupNames.ToArray();
foreach (var queryDupName in dupNames)
{
//分页开始
var currentLine = ; do
{
Console.Clear();
Console.WriteLine("Filename = {0}", queryDupName.Key.ToString() == string.Empty ? "[none]" : queryDupName.Key.ToString()); //跳过 currentLine 行,取 numLines 行
var resultPage = queryDupName.Skip(currentLine).Take(numLines); foreach (var fileName in resultPage)
{
Console.WriteLine("\t{0}", fileName);
} //增量器记录已显示的行数
currentLine += numLines; //让用户自动选择下一下
//Console.WriteLine("Press any key to continue or the 'End' key to break...");
//var key = Console.ReadKey().Key;
//if (key == ConsoleKey.End)
//{
// isAgain = false;
// break;
//} //按得有点累,还是让它自动下一页吧
Thread.Sleep(); } while (currentLine < queryDupName.Count()); //if (!isAgain)
// break;
} }

  

  图中执行结果可能与您的不一样。

  第一个查询使用一个简单的键确定是否匹配;这会找到同名但内容可能不同的文件。第二个查询使用复合键并根据 FileInfo 对象的三个属性来确定是否匹配。此查询非常类似于查找同名且内容类似或相同的文件。
 

七、如何在文件夹中查询文件的内容

  此示例演示如何查询指定目录树中的所有文件、打开每个文件并检查其内容。 此类技术可用于对目录树的内容创建索引或反向索引。 此示例中执行的是简单的字符串搜索。 但是,可使用正则表达式执行更复杂类型的模式匹配。 
             const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 12.0";
var dir = new DirectoryInfo(path);
var files = dir.GetFiles("*.*", SearchOption.AllDirectories); //待匹配的字符串
const string searchTerm = @"Visual Studio";
//搜索每个文件的内容。
//您也可以使用正则表达式替换 Contains 方法
var queryMatchingFiles = from file in files
where file.Extension == ".html"
let content = GetFileConetnt(file.FullName)
where content.Contains(searchTerm)
select file.FullName; //执行查询
Console.WriteLine("The term \"{0}\" was found in:", searchTerm);
foreach (var filename in queryMatchingFiles)
{
Console.WriteLine(filename);
}
         /// <summary>
/// 读取文件的所有内容
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
static string GetFileConetnt(string fileName)
{
//如果我们在快照后已删除该文件,则忽略它,并返回空字符串。
return File.Exists(fileName) ? File.ReadAllText(fileName) : "";
}

  图中执行结果可能与您的不一样。


================================================== 传送门分割线 ==================================================

LINQ 其它随笔 -  《开始使用 LINQ

        《进阶 - LINQ 标准查询操作概述

         《Linq To Objects - 如何操作字符串

================================================== 传送门分割线 ==================================================

【首联】http://www.cnblogs.com/liqingwen/p/5816051.html

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

[C#] Linq To Objects - 如何操作文件目录的更多相关文章

  1. [C#] Linq To Objects - 如何操作字符串

    Linq To Objects - 如何操作字符串 开篇语: 上次发布的 <LINQ:进阶 - LINQ 标准查询操作概述>(90+赞) 社会反响不错,但自己却始终觉得缺点什么!“纸上得来 ...

  2. C# LINQ学习笔记四:LINQ to OBJECT之操作文件目录

    本笔记摘抄自:https://www.cnblogs.com/liqingwen/p/5816051.html,记录一下学习过程以备后续查用. 许多文件系统操作实质上是查询,因此非常适合使用LINQ方 ...

  3. Linq To Objects

    一.什么是Linq To Objects 从根本上说,Linq To Objects表示一种新的处理集合的方法.采用旧方法,必须编写指定如何从集合检索数据的复杂的foreach循环.而采用Linq方法 ...

  4. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  5. LINQ to Entities 和LINQ to Objects 的区别

    本文资料来源:http://www.codeproject.com/Articles/246861/LINQ-to-Entities-Basic-Concepts-and-Features) LINQ ...

  6. Linq之Linq to Objects

    目录 写在前面 系列文章 linq to objects 总结 写在前面 上篇文章介绍了linq的延迟加载特性的相关内容,从这篇文章开始将陆续介绍linq to Objects,linq to xml ...

  7. 查询表达式和LINQ to Objects

    查询表达式实际上是由编译器“预处理”为“普通”的C#代码,接着以完全普通的方式进行编译.这种巧妙的发式将查询集合到了语言中,而无须把语义改得乱七八糟 LINQ的介绍 LINQ中的基础概念 降低两种数据 ...

  8. LINQ之LINQ to Objects(上)

    LINQ概述 LINQ,语言集成查询(Language Integrated Query),它允许使用C#或VB代码以查询数据库相同的方式来操作不同的数据源. 1.LINQ体系结构 从上图可以看出,L ...

  9. 从LINQ开始之LINQ to Objects(下)

    前言 上一篇<从LINQ开始之LINQ to Objects(上)>主要介绍了LINQ的体系结构.基本语法以及LINQ to Objects中标准查询操作符的使用方法. 本篇则主要讨论LI ...

随机推荐

  1. 浅谈 Fragment 生命周期

    版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...

  2. Web性能优化:图片优化

    程序员都是懒孩子,想直接看自动优化的点:传送门 我自己的Blog:http://cabbit.me/web-image-optimization/ HTTP Archieve有个统计,图片内容已经占到 ...

  3. Ajax 概念 分析 举例

    Ajax是结合了访问数据库,数据访问,Jquery 可以做页面局部刷新或者说是页面不刷新,我可以让页面不刷新,仅仅是数据的刷新,没有频繁的刷页面,是现在比较常用的一种方式做页面那么它是怎么实现页面无刷 ...

  4. ASP.NET MVC5+EF6+EasyUI 后台管理系统(72)-微信公众平台开发-消息处理

    系列目录 前言 Senparc.Weixin.MP SDK提供了MessageHandler消息处理类 在作者的Wiki中也详细说明了如何定义这个类,下面我们来演示,消息的回复,及效果 了解Messa ...

  5. 如何在网页中提取Email地址

    开博好久了,今天第一次发表技术文档,之前总是将一些好的事例保存在电脑,时间久了找起来也很麻烦,所以还是放在博客里进行归类比较方便,这样也能将自己在学习过程中的一些心得体会分享给大家,也能给需要的人一点 ...

  6. 算法与数据结构(七) AOV网的拓扑排序

    今天博客的内容依然与图有关,今天博客的主题是关于拓扑排序的.拓扑排序是基于AOV网的,关于AOV网的概念,我想引用下方这句话来介绍: AOV网:在现代化管理中,人们常用有向图来描述和分析一项工程的计划 ...

  7. C++随笔:.NET CoreCLR之GC探索(4)

    今天继续来 带大家讲解CoreCLR之GC,首先我们继续看这个GCSample,这篇文章是上一篇文章的继续,如果有不清楚的,还请翻到我写的上一篇随笔.下面我们继续: // Initialize fre ...

  8. Java程序员:工作还是游戏,是该好好衡量一下了

    前阵子我终于下定决心,删掉了硬盘里所有的游戏. 身为一个程序猿,每天都要和各种新技术打交道,闲暇时间,总还得看一下各大论坛,逛逛博客园啥的,给自己充充电.游戏的话,其实我自小就比较喜欢,可以算是一种兴 ...

  9. maven打包插件:appassembler

    1.打包成bat 打包命令:mvn clean package appassembler:assemble <plugin> <groupId>org.codehaus.moj ...

  10. Bluemix中国版体验(二)

    从上一篇到现在大概有一个多月了.时隔一个月再登录中国版Bluemix,发现界面竟然更新了,现在的风格和国际版已经基本保持一致!这次我们来体验一下Mobile Service.不过mobile serv ...