循环访问目录树
参考: http://msdn.microsoft.com/zh-cn/library/bb513869.aspx
循环访问目录树”的意思是在指定的根文件夹下,访问每个嵌套子目录中任意深度的所有文件。不必打开每一个文件,只检索string 形式的文件名或子目录名,或者可以检索 System.IO.FileInfo 或 System.IO.DirectoryInfo 对象形式的其他信息。
如果确信拥有指定根目录下所有目录的访问权限,则您可以使用 System.IO.SearchOption.AllDirectories 标志。该标志返回与指定模式相匹配的所有嵌套子目录。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class RecursiveFileSearch { static System.Collections.Specialized.StringCollection log = new System.Collections.Specialized.StringCollection(); static void Main() { // 从驱动端搜索整个电脑 string[] drives = System.Environment.GetLogicalDrives();//从根目录下面开始 foreach (string dr in drives) { System.IO.DriveInfo di = new System.IO.DriveInfo(drives[0]); //跳过不是只读文件 // 这个参数可变,变为需要的 if (!di.IsReady) { Console.WriteLine("The drive {0} could not be read", di.Name); continue; } System.IO.DirectoryInfo rootDir = di.RootDirectory; WalkDirectoryTree(rootDir); } // 写出不能被处理的文件 Console.WriteLine("Files with restricted access:"); foreach (string s in log) { Console.WriteLine(s); } // 在调试模式下打开控制窗口 Console.WriteLine("Press any key"); Console.ReadKey(); } static void WalkDirectoryTree(System.IO.DirectoryInfo root) { System.IO.FileInfo[] files = null; System.IO.DirectoryInfo[] subDirs = null; // 首先,处理所有在这个文件夹中的文件 try { files = root.GetFiles("*.docx"); } // 当一个文件需要访问权限比较大时,则会抛出。 catch (UnauthorizedAccessException e) { //这段代码只写出消息并继续递归。你可以决定做些不同的事情。例如,你可以试着提高你的权限和访问文件。 log.Add(e.Message); } catch (System.IO.DirectoryNotFoundException e) { Console.WriteLine(e.Message); } if (files != null) { foreach (System.IO.FileInfo fi in files) { //在这个例子中,我们只访问现有FileInfo对象。 //如果我们想要开放、删除或修改文件,然后需要一个try - catch块来处理的文件已被删除从调用TraverseTree()。 Console.WriteLine(fi.FullName); } // 现在找到这个目录下所有的子目录。 subDirs = root.GetDirectories(); foreach (System.IO.DirectoryInfo dirInfo in subDirs) { // Resursive呼吁每个子目录。 WalkDirectoryTree(dirInfo); } } } }
首先注意这里面用了StringCollection集合,
System.Collections.Specialized.StringCollection log = new System.Collections.Specialized.StringCollection();具体请参考:此处。
找到集合后可以过滤要找的文件,用DirectoryInfo对象 root对象的GetFiles(“*.*”),此处可以过滤想要找的文件,返回类型是FileInfo,也就是说接下来就可以操作想要的对象了,例如:"*.txt"。
递归遍历目录树:递归方法很简洁,但如果目录树很大且嵌套很深,则有可能会引起堆栈溢出异常。对于所处理的特定异常以及在每个文件和文件夹上执行的特定操作,都只是作为示例提供。 您应该修改此代码来满足自己特定的需要。
获取有关文件、文件夹和驱动的信息
参考:http://msdn.microsoft.com/zh-cn/library/6yk7a1b0.aspx
在 .NET Framework 中,可以使用以下类来访问文件系统信息:
- System.IO.FileInfo
- System.IO.DirectoryInfo
- System.IO.DriveInfo
- System.IO.Directory
- System.IO.File
File: 提供用于创建、复制、删除、移动和打开文件的静态方法,并协助创建 FileStream 对象。
FileInfo:提供创建、复制、删除、移动和打开文件的属性和实例方法,并且帮助创建 FileStream 对象。
Directory:公开用于创建、移动和枚举通过目录和子目录的静态方法。 此类不能被继承。
DirectoryInfo:公开用于创建、移动和枚举目录和子目录的实例方法。
DriveInfo:提供对有关驱动器的信息的访问。
System.IO.Directory 和 System.IO.File 类提供用于检索有关目录和文件的信息的静态方法。只说一点,静态类的方法可以共享。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class FileSysInfo { static void Main() { System.IO.DriveInfo di = new System.IO.DriveInfo(@"C:"); Console.WriteLine(di.TotalFreeSpace); Console.WriteLine(di.VolumeLabel); // 得到根目录,打印出相关信息 System.IO.DirectoryInfo dirInfo = di.RootDirectory; Console.WriteLine(dirInfo.Attributes.ToString()); // 目录中的文件盒打印一些信息 System.IO.FileInfo[] fileNames = dirInfo.GetFiles("*.*"); foreach (System.IO.FileInfo fi in fileNames) { Console.WriteLine("{0}: {1}: {2}", fi.Name, fi.LastAccessTime, fi.Length); } //得到的直接子目录下的根。 System.IO.DirectoryInfo[] dirInfos = dirInfo.GetDirectories("*.*"); foreach (System.IO.DirectoryInfo d in dirInfos) { Console.WriteLine(d.Name); } //目录和文件类提供一些静态方法来访问文件和目录。 // 获取当前应用程序目录。 string currentDirName = System.IO.Directory.GetCurrentDirectory(); Console.WriteLine(currentDirName); //是一个文件名作为字符串数组而不是FileInfo对象。 //使用这种方法,当存储空间是一个问题,当你尝试访问该文件,可能会用文件名引用一段时间。 string[] files = System.IO.Directory.GetFiles(currentDirName, "*.txt"); foreach (string s in files) { // 只在需要时创建FileInfo对象,以确保尽可能是当前的信息。 System.IO.FileInfo fi = null; try { fi = new System.IO.FileInfo(s); } catch (System.IO.FileNotFoundException e) { Console.WriteLine(e.Message); continue; } Console.WriteLine("{0} : {1}", fi.Name, fi.Directory); } //改变目录。 //在这种情况下,首先检查是否已经存在,如果不存在则创建它。 if (!System.IO.Directory.Exists(@"C:UsersPublicTestFolder")) { System.IO.Directory.CreateDirectory(@"C:UsersPublicTestFolder"); } //将当期目录设定为指定目录 System.IO.Directory.SetCurrentDirectory(@"C:UsersPublicTestFolder"); currentDirName = System.IO.Directory.GetCurrentDirectory(); Console.WriteLine(currentDirName); // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); } }
DriveInfo只是获得驱动,GetDrives()只是获取所有逻辑驱动器的驱动器名称,和驱动相关的都可以用这个类来操作。
Directory目录篇
DirectoryInfo和Directory说白了就是读取指定目录下的文件目录,例如上面第一段代码中的递归方法,就是用来访问文件目录的。此类可以创建目录或删除目录,操作的只是目录而不是对象本身。目录通常由各种文件组成。
常用的方法有:
create()——创建目录。
void Delete(string path,bool recursive)删除目录,recursive表示是否递归删除,如果recursive为false则只能删除空目录。
bool Exists(string path) 判断目录是否存在
string[] GetDirectories(string path)得到一个目录下的子目录
string[] GetDirectories(string path,string searchPattern,SearchOption searchOption) 通匹配符查找子目录下的子目录,可以搜索到隐藏文件
static string[] GetFiles(string path)得到一个父目录下的文件
string[] GetFile(string path,string searchPattern,SearchOption searchOption) 通过匹配符查找目录下的文件
DirectoryInfo GetParent(string path) 得到目录的父目录
move()//移动、剪切。只能在同一个磁盘中。目录没有Cope方法。可以使用Move()方法实现重命名。
//可以指定一个目录当做对象来使用
DireectoryInfo dirInfo=new DirectoryInfo(@"c:abc")
//通过dirInfo对象,就可以获取c:abc下的所有的文件与文件夹
FileInfo[] finfos=dirInfo.GetFiles();
DirectoryInfo[] dinfos=dirInfo.GetDirectories();
实例代码:TreView上加载目录的代码,用的递归和过滤*.txt文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
代码获取文件: private void Load(object sender,EventArgs e) { //将Demo下的文件夹加载到TreeView上 //获取demo目录 DirectoryInfo demoDir = new DirectoryInfo("demo")' //获取demo目录下的所有的直接子文件夹 DirectoryInfo[] dirInfos=demoDir.GetDirectories(); foreach(DirectoryInfo item in dirIndos) {TreeNode tode=treeView1.Nodes.Add(item.Name); LoadData(item.FullName,tnode);}} //递归获取当前目录下的所有子目录和子文件 private void LoadDate(string path,TreeNode tnode) { //获取当前路径下的所有直接子文件 string[] files=Directory.GetFiles (path,"*.txt",SearchOption.TopDirectoryOnly); //把这些文件加到treeView上 foreach(string item in files) { TreeNode nodeFile=tnode.Nodes.Add(Path.GetFileName(item)); //当前文件的完整路径绑定到Tag中,Tag在每个控件中都有,没有意义就是为了存放//数据 nodeFile.Tag=item; } 2.获取当前路径下的所有直接子文件夹 string[] dirs=Directory.GetDirectories(path); foreach(string item in dirs) {//Add方法的返回值就是,刚刚增加的这个节点 TreeNode node=tnode.Nodes.Add(Path.GetFileName(item)); LoadDate(item,node); }}
File文件篇
文件编码:
ASCLL英文码表,每个字符占1个字节。
GB2312,兼容ASCLL。包含中文。每个英文占一个字节,中文占两个字节。
GBK:简体中文,兼容gb2312,包含更多汉子。英文占1个字节,中文占两个。
Big5繁体中文。
Unicode:国际码表,中文英文都占两个字节。
Utf-8:国际码表,英文占一个字节,中文占3个字节。
方法:
void AppendAllText(string path,string contents),将文本contents附加到文件path中。
bool Exists(string path)判断文件path是否存在。
string[] ReadAllLine(string path)读取文本文件到字符串数组中。
string ReadAlText(string path)读取文本文件到字符串中。
void WriteAlText(string path,string contents)将文本contents保持到文件path中,会覆盖旧文件。
WriteAllLine(string path,string[] contents),将字符串数组逐行保存到文件path中,会覆盖旧内容。
File.Delete(@"c:1.txt");//如果不存在也不报异常
文件拷贝:
拷贝文件的两种方式:
将源文件全部读到内存中,在写到目标文件中;
读取源文件的Nkb内存,写到目标文件中,在读取文件的Nkb内存,在写到目标文件中。(第二种方式就是一种流的的操作)
用File.ReadAllText、File.WriteAllText进行文件读写是一次性读写,如果文件非常大会占内存、慢。若需要读取一次处理一行
的机制,这就是流(Stream)。Stream会只读取要求的位置、长度的内容。Stream不会将所有内容一次性读取到内存中,还有一个指针
,指针指到哪里才能读、写到哪里。流有很多种,文件流是其中一种(还有二进制流、网络流等等)。常用FileStream类的new FileStream("c:/a.txt",filemode,fileaccess)后两个参数可选值。filestream可读可写。可以使用file.OpenReead、File.OpenWrite这两个简化调用方法。用byte[]是任何数据的最根本表示形式,任何数据最终都是二进制。filestream的Position属性为当前文件指针位置,没写一次就要移动一下Position以备下次写到后面的位置。Write用于向当前位置写入若干字节,Read用户读取若干字节。
通过文件流读取磁盘上的文件:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//1.创建文件流 FileMode可以选择打开文件的方式 FileStream fsRead=new FileStream("1.txt",FileMode.Open); //2创建缓冲区 byte[] byts=new byte[fsRead.Length]; //开始读取 要byts读取到的数组中 int r=fsRead.Read(byts,0,btys.Length); //关闭文件流,释放资源 fsRead.Colse(); //将byte文件转换为文字 string msg=System.Text.Encoding.UTF8.GetString(byts);
通过文件流写入文件:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//创建文件流 FileStream fs=new FileStream(@"c:mywords.txt",FileMode.OpenOrCreate); string msg="wenzi";//要写入的内容 //创建byte[] byte[] byts= System.Text.Encoding.UTF8.GetBytes(msg); //将byts中的内容写入文件中 fs.Write(byts,0,byts.Length); //关闭文件流 fs.Flush();//清除下缓存,都写到硬盘中 fs.Close(); fs.Dispose();
拷贝大文件代码 ,需要两个流:一个用来读,一个用来写。两个文件,两个文本。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//创建一个读取文件的流 using(FileStream fsRead=new FileStream(source,FileMode.Open)) { //创建一个写入文件的流 using(FileStream fsWrite=new FileStream(target,FileMode.Create)) { //创建一个读取文件、写入文件的一个缓冲区 byte[] buffer=new byte[1024*1024 *10];//10M //源文件的总字节数 long len=fsRead.Length; //开始读取文件 while(true) { //返回值t表示本次实际读取到的字节数 int r=fsRead.Read(buffer,0,buffer.Length); //将读取出来的buffer内容写入到fsWrite文件流中。 if(r<=0)//表示读取到了文件的末尾 {break;} else{ //如果r>0,则表示本次读取到了内容 fsWrite.Write(buffer,0,r); //已经拷贝了的文件长度 //long lenAlready=fsWrite.Length; //double proc=(double)lenAlready/len; double proc=(double)fsWrite.Position/len; Position当前流的位置 Console.WriteLine("拷贝进度{0}",proc*100); }}}}
流里面有一个Position属性,用来确定读取到哪了:fsRead.Position。这个属性可以记得上次读取到哪了,再次读取时可以从上次读取的位置进行读取。
用StreamWrite可以简化文本类型的Stream的处理。StreamReader是辅助Stream进行处理的,Steam读取的不能分辨出读取到哪,可能是半个字。StreamReader是读取文本一次只读取一行。StreamReader sr =new StreamReader("c:writer.txt");
sr.EndOfStream //没有读取到文件流末尾,就继续读取
while(sr.ReadLine()!=null){
Console.WriteLine(sr.ReadLine());//判断处已经读取一次,所以会间接读取}
File类的方法:快速得到文件流
FileStream fs=File.Open(); //返回FileStream
FileStream fs=File.OpenRead(); //返回只读的FileStream
FileStream fs=File.OpenWrite(); //返回只读的FileStream
FileStream fs=new FileStream(参数);
获取当前文件的所在路径注意事项:如获取当前exe文件执行的路径。 两种方法:
Assembly.GetExecutingAssembly().Location;
Application.StartupPath。
注意:不要用Directory.GetCurrentDirectory();获取应用程序的当前工作目录,因为这个可能会变。如果设置OpenFileDialog对话框的路径的话,用Directory.GetCurrentDirectory()将得不到exe准确路径,这个需要特别注意下。
//textBox2.Text=File.ReadAllText("1.txt",Encoding.Default);
//获取当前的exe文件执行的路径
string exePath=Assembly.GetExecutingAssembly().Location.ToString();
string txtPath =Path.Combine(Path.GetDirectoryName(exePath),"1.txt");
textBox2.Text=File.ReadAllText(txtPath,Encoding.Default);
Path类部分
对包含文件或目录路径信息的 String 实例执行操作。 这些操作是以跨平台的方式执行的。
Path类中方法,基本都是对字符串的操作,与实际文件都没关系。在Path类中修改文件的路径是没有作用的。Path类,目录和文件操作的命名空间System.IO;
常用的Path方法:
Path.GetTempPath(); //获取当前用户的临时目录
Path.GetTempFileName();//获取一个随机额文件名,并在临时目录下创建这个文件
Path.GetRandomFileName();//获取一个随机的文件名(也可以用作文件名。
string ChangeExtension(string path,string axtension) //修改文件的后缀,"修改"支持字符串层面的,没有真的给文件改名
string Combine(String path1,stirng path2) //将两个路径合并成一个路径 ,比用+好,可以方便解决不加斜线的问题,自动处理路径分隔符的问题,如:string s=Path Combine(@"c:temp","a.jpg")
string GetDirectoryName(string Path){}得到文件的路径名,Path.GetDirectoryName(@"c:temoa.jpg")
string GetExtension(string path) 得到文件的扩展名
string GetFileName(string path) 得到文件路径的文件名部分
string GetFileNameWithoutExtension(string path)得到去除扩展名的文件名
string GetFullPath(string path)得到文件的全路径,可以根据相对路径获得绝对路径
Path.Combine(path1,path2)如果指定的路径之一是零长度字符串,则该方法返回其他路径。如果path2包含绝对路径,则该方法返回path2.
如果path1不是以分隔符结束,并且不是c:或d:等则在串联钱path1增加分隔符。
分隔符:(与操作平台有关)
Path.DirectorySeparatorChar→
Path.PathSeparator→;
Path.VolumeSeparatorChar→:
Path.GetFileName()
获取文件名当目录为c:windows est时,可获取最后一个目录名,但当目录路径为c:windows est时,不可以。
流部分
stream(所有流的父类,是一个抽象类)
可以查看此处:http://www.cnblogs.com/Johnny_Z/archive/2011/09/12/2174148.html,不想再整了。没必要整写重复的东西。
序列化部分
参考:http://msdn.microsoft.com/zh-cn/library/7ay27kt9(v=vs.90).aspx
序列化是将对象状态转换为可保持或传输的形式的过程。序列化的补集是反序列化,后者将流转换为对象。这两个过程一起保证数据易于存储和传输。
.NET Framework 提供了两个序列化技术:
-
二进制序列化保持类型保真,这对于多次调用应用程序时保持对象状态非常有用。例如,通过将对象序列化到剪贴板,可在不同的应用程序之间共享对象。您可以将对象序列化到流、磁盘、内存和网络等。远程处理使用序列化,“按值”在计算机或应用程序域之间传递对象。
-
XML 序列化只序列化公共属性和字段,并且不保持类型保真。当您希望提供或使用数据而不限制使用该数据的应用程序时,这一点非常有用。由于 XML 是开放式的标准,因此它对于通过 Web 共享数据来说是一个理想选择。SOAP 同样是开放式的标准,这使它也成为一个理想选择。
如何序列化:http://msdn.microsoft.com/zh-cn/library/szzyf24s(v=vs.90).aspx
如何反序列化:http://msdn.microsoft.com/zh-cn/library/fa420a9y(v=vs.90).aspx
小总:哪些类可被序列化?
1.必须本身是可序列化的(Person类型标记为可序列化)
2.类中所有的字段属性的类型也必须标记为可序列化的
3.当前类型的所有父类也必须标记为可序列化的。
例如:
[Serializable]
public calss Person
{//2.建议,在使用序列化的时候尽量不要使用自动化属性,因为自动属性
,每次编译的时候自动生成的字段名可能不一样,所以在反序列化的时候可能会造成问题
Public string Name//自动属性
{get;set;}}
反序列化
BinaryFormatter bf=new BinaryFormatter();
using(FileStream fs=new FileStream("person.bin",FileMode.Open))
{2.执行反序列化
Person per=(Person)bf.Deserialize(fs);}
在执行反序列化的时候,由于person.bin中存储的是原来Person类型序列化后的结果,所以要对person.bin反序列化时,需要person类所在的程序集。
问题:为什么反序列化的时候需要原来定义Person类的那个程序集?
反序列化的时候,要返回一个对象,这个对象必须根据原来Person所在的程序集才能创建,也就是说person.bin中所包含的仅仅是一些数据,根据这些数据是无法在内存中创建对象的。只能是根据原来Person类型的程序集来创建对象,然后Person.bin中的数据——对应进行赋值。
对象序列化,只是对对象的一些状态信息进行序列化(比如:字段)。
对于方法之类的根本不能进行序列化,也就是说person.bin中只包含字段名和字段值,根本没有 方法信息。但是反序列化的时候返回的是一个对象,那么只根据这些字段信息是无法创建对象的,所以这个时候需要先根据原来的程序集,获取Person类型相信n对象,然后再把反序列化得到的字段信息赋值给这个Person对象。
把对象当做字节流。
对象序列化是将对象(比如Person对象)转换为二进制数据(字节流),反序列化是将二进制数据还原为对象。对象是稍纵即逝的,不仅程序重启、操作系统重启会造成对象的消失,就是对出函数范围等都可能会造成对象的消失,序列化/反序列化就是为了保持对象的持久化。就像用dv录像(序列化)和用播放器播放(反序列化)一样。
对象序列化,只能针对对象的字段进行序列化。
BinaryFormatter类有两个方法;
void Seriallize(Stream stream,object graph)对象graph序列化到stream中。
object Deseriallize(Stream stream)将对象从stream中反序列化,返回值为反序列化得到的对象。
不是所有对象都能序列化,只有可序列化的对象才能序列化,在类声明上添加[Serializable],对象的属性、字段的类型也必须可序列化。
关于二进制序列化需要注意的事项:
1.要序列化的类型必须标记为:【Serializable】
2.该类型的父类也必须标记为:【Serializable】
3.该类型中的所有成员的类型也必须标记为:【Serializable】
4.序列化只会对类中的字段序列化。(只能序列化一些状态信息)
5.不建议使用自动属性。(每次生成的字段都可能不一样,影响反序列化)
为什么要序列化?
将一个复杂的对象转换流,方便我们的存储于信息交换。
序列化的应用:
-
- 序列化的应用:Asp.Net、ViewState、WCF、.Net Remoting、Asp.Net Cache、集群等。
- 将对象持久化存储、磁盘、数据库
- 将对象复制到剪贴板
- 将对象通过网络传输
- 将对象备份(深拷贝)
对象的序列化和反序列化:
1.创建一个二进制序列化器:
BinaryFormatter bf=...;
2.创建一个文件流
3.bf.Serialize(stream,对象);
更多参考:http://msdn.microsoft.com/zh-cn/library/7ay27kt9(v=vs.90).aspx