计算机以文件的形式把数据存储在磁盘、光盘等存储设备上。文件的管理和操作是操作系统的一个重要组成部分,.NET 框架提供了一组功能强大的类,可以方便地对文件进行操作和管理。

1.文件操作相关的类

用于文件操作的类位于System.IO 命名空间中,用这些类可以方便地对文件进行创建、读写、复制、删除、移动、打开等操作。

2.File类和FileInfo类

命名空间 System.IO 中的File 类用于对文件进行创建、打开、复制、移动、删除、重命名等典型操作,并能获取或设置文件的属性信息。File 类中所有的方法都是静态的,使用起来非常简单,File 类的部分方法如下图所示:

File类的方法使用起来非常方便:

using System;

using System.IO;

static void Main(string[] args)

{  string path = @"D:\test.txt";

if(File.Exists(path)) //如果文件已经存在,则读取文件内容

{  //读取文件

string contents = File.ReadAllText(path);

Console.WriteLine("读取文件:\n" + contents);  }

else //如果文件不存在,则创建文件并写入内容

{  string contents = "无可奈何花落去,\n 似曾相识燕归来,\n 小园香径独徘徊。";

File.WriteAllText(path, contents); //写入文件

Console.WriteLine("文件已写入。" );  }

}

注意 WriteAllText()、WriteAllLines()和WriteAllBytes()方法都会覆盖以前的文件,使用时要特别小心。要想在文件尾部追加新文本,请使用AppendAllText()方法。

FileInfo 类和File 类相似,可以创建、复制、移动、删除文件,可以获取或设置文件的属性,只是少了读写文件的功能。FileInfo 类的成员方法都是非静态的,使用方法前需要先创建一个FileInfo 类的对象,然后通过FileInfo 对象调用方法。当频繁操作某文件时,使用FileInfo 类的效率会更高。

(1)关于文件的异常

操作文件时经常会发生异常,比如指定文件不存在,路径无效,没有权限等。关于文件的异常的方法如图示,因此关于文件的操作一般要放在 try-catch 结构中,以处理可能发生的异常情况。

try

{  string path = @"D:\test.txt";

//如果文件已经存在,则读取文件内容

if(File.Exists(path))

{  //读取文件

string contents = File.ReadAllText(path);

Console.WriteLine("读取文件:\n" + contents);

}

else //如果文件不存在,则创建文件并写入内容

{

string contents = "无可奈何花落去,\n 似曾相识燕归来,\n 小园香径独徘徊。";

//写入文件

File.WriteAllText(path, contents);

Console.WriteLine("文件已写入。" );

}

}

catch (Exception e)

{  //异常处理

Console.WriteLine(e.Message);

}

3.Directory类和DirectoryInfo类

System.IO 命名空间中的Directory 类用于执行对目录(文件夹)的操作,比如创建、移动、删除、重命名等,也可通过它获取或设置目录的属性。Directory 类的部分方法如图所示,所有 Directory 类中的方法都是静态的,使用起来非常方便。

using System;

using System.IO;

static void Main(string[] args)

{  try

{  string path = @"D:\Program Files\Windows Media Player";

if (Directory.Exists(path)

{  //获取子目录

string[] dirs = Directory.GetDirectories(path);

Console.WriteLine("子目录:");

foreach (string dir in dirs) {

Console.WriteLine(dir); }

//获取文件

string[] files = Directory.GetFiles(path);

Console.WriteLine("文件:");

foreach (string file in files) {Console.WriteLine(file);}

}

else{Console.WriteLine("目录不存在");}

}

catch (Exception e)

{//异常处理Console.WriteLine(e.Message);}

}

操作结果所图所示,DirectoryInfo 类和Directory 类功能相似,区别是DirectoryInfo 类的成员方法都是非静态的,使用方法前需要先创建一个DirectoryInfo 类的对象。当频繁操作某目录时,使用DirectoryInfo 类的效率会更高。

4.path类

通过 System.IO 命名空间中的Path 类,我们可以方便的处理路径。Path 类的部分字段和方法如图所示。Path 类中的很多字段是和操作系统关联的,在不同的操作系统中可能有不同的结果。比如DirectorySeparatorChar,在Windows 和Macintosh 操作系统中的值是“\”,在Unix 操作系统中为“/”;VolumeSeparatorChar 在Windows 和Macintosh 操作系统中为“:”,在Unix操作系统中为“/”;PathSeparator 在 Windows 操作系统中默认值是“;”,在 Unix 操作系统上为“:”。

using System.IO;

//输出分隔符

Console.WriteLine("DirectorySeparatorChar" + Path.DirectorySeparatorChar);

Console.WriteLine(" PathSeparator" + Path.PathSeparator);

Console.WriteLine(" VolumeSeparatorChar" + Path.VolumeSeparatorChar);

//输出路径信息

string path = @"D:\Program Files\Windows Media Player\wmplayer.exe";

Console.WriteLine(" GetFileName" + Path.GetFileName(path));

Console.WriteLine(" GetFileNameWithoutExt" + Path.GetFileNameWithoutExtension(path));

Console.WriteLine(" GetExtension" + Path.GetExtension(path));

Console.WriteLine(" GetDirectoryName" + Path.GetDirectoryName(path));

结果如图所示:

(1)Environment类

通过 System 命名空间中的Environment 类,可以方便地获取与系统相关的信息。Environment 类的部分属性和方法如图所示。

一个例子:

//显示系统信息

Console.WriteLine(" 处理器数量: " + Environment.ProcessorCount);

Console.WriteLine(" 操作系统版本: " + Environment.OSVersion);

Console.WriteLine("公共语言运行库版本: " + Environment.Version);

Console.WriteLine(" 系统目录: " + Environment.SystemDirectory);

Console.WriteLine(" 用户域名: " + Environment.UserDomainName);

Console.WriteLine(" 用户名: " + Environment.UserName);

Console.WriteLine(" 机器名: " + Environment.MachineName);

通过 Environment 类的GetFolderPath()方法可以获取系统特殊文件夹的路径,这些特殊文件夹由SpecialFolder 枚举列出。SpecialFolder 类的部分成员如图所示。

例子:

string d = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

string p = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);

string m = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic);

string f = Environment.GetFolderPath(Environment.SpecialFolder.Favorites);

//输出特殊目录路径

Console.WriteLine("我的文档:" + d);

Console.WriteLine("我的图片:" + p);

Console.WriteLine("我的音乐:" + m);

Console.WriteLine("我的收藏:" + f);

5.基于流的文件操作

数据以文件的形式存储在硬盘、光盘等存储介质上,读写数据的过程可以看作数据像水一样流入或流出存储介质,所以.NET 设计了一种叫做流(Stream)的类来读写文件。实际上一旦我们打开一个文件,它就和一个流关联起来。文件是数据源,流是传输数据的工具,通过这种专门的工具来传输数据,就可以使传输工具和数据源分离开来,从而更容易切换数据源,更容易实现不同环境下代码的重用。数据源除了文件以外,还可以是内存中数据、网络上的数据甚至是代码中的一个变量等等。与流相关的类都定义在 System.IO 命名空间中,它们大多数继承于抽象类Stream。如图展示了部分与流相关的类。

FileStream 类用于字节数据输入和输出,TextReader 和TextWriter 用于Unicode 字符的输入和输出,BinaryReader 和BinaryWriter 用于二进制数据的输入和输出。

(1)FileStream类

FileStream 是专门进行文件操作的流,使用它可对文件进行读取、写入、打开和关闭等操作,既支持同步读写操作,也支持异步(缓冲)读写操作。FileStream 类的部分属性和方法如图所示:

要使用流,必须先创建一个流的对象。请创建一个名为“StreamTest”的控制台项目,并添加如下代码。

string fileName = @"D:\filestream_test.data";

//若文件不存在,则创建文件,写入数据

if(!File.Exists(fileName))

{  FileInfo myFile = new FileInfo(fileName); //创建文件

FileStream fs = myFile.OpenWrite(); //获取与文件对应的流

byte[] datas = { 100, 101, 102, 103, 104, 105, 106, 107, 108, 109 };

fs.Write(datas, 0, datas.Length); //用流写入数据

Console.WriteLine("数据已写入。");

fs.Close(); //关闭流

}

else //若文件存在,则读取数据

{   //建立与文件关联的流

FileStream fs = new FileStream(fileName, FileMode.Open, File Access.Read);

byte[] datas = new byte[fs.Length];

fs.Read(datas, 0, datas.Length); //用流读取数据

Console.WriteLine(“读取数据:”);

foreach(byte data in datas)

{  Console.Write(data);}

fs.Close();  //关闭流

}

这里我们用两种方法获取与文件对应的流。第一种办法是通过FileInfo类的OpenWrite()方法获取,OpenWrite()方法打开相应的文件,返回一个与该文件相关的FileStream对象。

当使用FileStream类的构造函数时,需要提供打开方式(FileMode)、访问模式(FileAccess)、共享模式(FileShare)等信息,它们分别用FileMode枚举、FileAccess枚举和FileShare枚举表示,这些枚举的值很容易理解,更详细的信息请参看帮助文档。与FileStream类相关的枚举如图所示:

FileStream类的Write()方法和Read()方法。Write()方法的原型如下,参数中字节数组array 是数据源,offset 和count 表示把数据源中的从位置offset 开始的count 个字节写入文件。

Read()方法的原型如下:参数中字节数组array 是目标数组,用于存储读取到的数据,存储的位置是从位置offset开始的count 个字节。

返回值为实际读取到的字节数。如果开始读取时当前位置已在流的末尾,则返回值为0。如果流中当前可用的字节数没有请求的字节数那么多,则实际读取到的字节数会小于请求的字节数。还有一个需要注意的情况是,在处理网络流(如FTP)、设备流(如串口输入)等流时,即使尚未到达流的末尾,实际读取到的字节数仍有可能少于请求的字节数。如果是从控制台读取或者从本地文件读取,很少遇到这种情况。

综上可以看出,FileStream 类主要用于向文件中写入或读取以字节为单位的数据。

2)关于流的异常

try-catch-fianlly结构,用流来操作文件也可能会出现异常,所以关于流的操作一般要放在try-catch-fianlly 结构中,以增强程序的健壮性。

string fileName = @"D:\filestream_test.data";

FileStream fs = null;

try

{…}

catch(Exception e)

{ Console.WriteLine(e.Message);}

finally

{ if (fs!=null)  fs.Close(); }

注意,fs对象的声明代码要放在try语句之前,不然catch块和fianly块中无法识别。另外关闭流的操作应放在fianlly块中,以确保无论操作成功还是操作失败,流都被及时释放。

读取一个文件的内容时,真正有用的代码往往只有几行,然而为了健壮性,我们却要写上10多行的代码来处理异常和关闭文件。如何才能减少这种麻烦呢?一种办法是自己编写一个方法,把异常处理和关闭文件的代码封装进去。下面编写了一个处理文件的通用方法UniversalProcess(),只要告诉它文件路径和处理文件的具体代码,它就会自动完成异常处理和关闭文件的操作。

public delegate void MyFileProcessCode(FileStream file);

//封装文件处理

public static void UniversalProcess(string path,MyFileProcessCode doSomething)

{ FileStream fs=new FileStream(path,FileMode.Open,FileAccess.ReadWrite);

try {  doSomething(fs);}

catch(Exception e)  {Console.WriteLine(e.Message);}

finally{if(fs!=null) fs.Close();}

}

可以直接通过匿名函数和一个文件地址进行访问,例如:

UniversalProcess(@"D:\a.text", delegate(FileStream fs)

{

byte[] datas = { 100, 101, 102, 103, 104, 105, 106, 107, 108, 109 };

fs.Write(datas, 0, datas.Length);

});

6.using语句

为了防止出现同步问题,当一个程序读写文件时,操作系统通常都会阻止其他程序读写该文件,因此使用完毕后要及时关闭,否则就会导致其他程序不能使用该文件。除了可以在fianlly块中关闭文件(流)外,我们还可以用C#提供的using语句进行文件操作。

using(FileStream fs = File.OpenWrite(path))

{

byte[] datas = { 100, 101, 102, 103, 104, 105, 106, 107, 108, 109 };

fs.Write(datas, 0, datas.Length);

}

在上面的代码在using后的括号里创建了一个流对象fs,然后在using后的语句块中使用该流对象。当退出using语句时,系统会自动关闭流对象fs,使用起来非常方便。using语句适用于那些需要及时释放资源的代码,其一般形式为:

using(type obj = initialization)

{ //具体处理代码}

在using后的括号中创建一个对象obj,然后在后面的大括号中使用它。当退出using语句时,对象obj会被及时销毁。using语句实际上是对try语句的封装,等价于:

{ type obj=initialization;

try {//具体处理代码}

finally{ if (obj!=null) { ((IDisposable)obj).Dispose();}}

}

程序先执行try块中的代码,不管是否发生异常,系统都会自动执行finally块中的代码,调用obj对象的Dispose()方法,销毁对象,释放资源。Dispose()方法和Close()方法的功能基本上是一样的,都是用来销毁对象。实际上,Close()方法就是通过调用Dispose(),方法实现的。

由此看出 using 语句不光适用于处理关于流的操作,它也适用于任何包含Dispose()方法且需要及时销毁的的对象。using 语句使用起来非常方便,如果你查看帮助文档,就会发现涉及流的例子都是用using 语句编写的。

7.用流读写文本文件

文本文件主要通过 StreamReader 和StreamWriter 来实现读写,它们分别继承于抽象类TextReader 和TextWriter。StreamReader 类的部分属性和方法,StreamWriter 类的部分属性和方法,分别所图示。

要使用 StreamReader 和StreamWriter,也必须先创建它们的对象。

StreamWriter streamWriter = null;

StreamReader streamReader = null;

try

{

//若文件不存在,则创建文件,写入数据

if(!File.Exists(@"D:\text_test.txt"))

{

FileInfo myFile = new FileInfo(@"D:\text_test.txt"); //创建文件

streamWriter = myFile.CreateText(); //获取文件对应的流

string text = @"何处望神州,满眼风光北固楼。千古兴亡多少事,悠悠。不尽长江滚滚流。年少万兜鍪,坐断东南战未休。天下英雄谁敌手?曹刘。生子当如孙仲谋。";

streamWriter.Write(text); //用流写入数据

Console.WriteLine("数据已写入.");

}

else//若文件存在,则读取数据

{  //建立与文件关联的流

streamReader = new StreamReader(@"D:\text_test.txt");

string text = streamReader.ReadToEnd(); //用流读取数据

Console.WriteLine("读取数据:\n" + text);

}

}

catch (Exception e)

{  //异常处理

Console.WriteLine(e.Message);

}

finally

{  //关闭流

if(streamWriter!=null)streamWriter.Close();

if(streamReader!=null)streamReader.Close();

}

这里我们用两种方法获取与文件对应的流。第一种办法是通过FileInfo 类(或File 类)的方法获取,比如FileInfo 对象myFile的CreateText()方法返回一个StreamWriter 流:StreamWriter streamWriter =myFile.CreateText(); OpenText ()方法返回StreamReader 流:StreamReader streamReader=myFile.ReadText(); 。File 类的部分与流相关的方法如图所示。

第二种办法是用 StreamReader 类或StreamWriter 类的构造函数创建流。

StreamReader streamReader = new StreamReader(fileName);

8.用流读写二进制文件

通过 BinryReader 和BinaryWriter 类实现二进制读写。BinryReader 类的部分属性和方法,BinaryWriter 类的部分属性和方法,分别如图所示:

要使用 BinryReader 和BinaryWriter 类,也必须先创建它们的对象,但它们的对象总是通过FileStream 对象来创建的。

string path = @"D:\binary_test.txt";

FileStream fileStream = null;

try

{  //创建文件

fileStream = File.Create(path);

//写文件

BinaryWriter bw = new BinaryWriter(fileStream);

DateTime time = DateTime.Now;

bw.Write(time.Year);

bw.Write(time.Month);

bw.Write(time.Day);

bw.Write(time.Hour);

bw.Write(time.Minute);

bw.Write(time.Second);

//当前位置移动到开头

fileStream.Seek(0, SeekOrigin.Begin);

//读文件

BinaryReader br = new BinaryReader(fileStream);

int year = br.ReadInt32();

int month = br.ReadInt32();

int day = br.ReadInt32();

int hour = br.ReadInt32();

int minute = br.ReadInt32();

int second = br.ReadInt32();

Console.WriteLine("{0}-{1}-{2} {3}:{4}:{5}",

year, month, day, hour, minute, second);

}

catch (Exception e)

{

//异常处理

Console.WriteLine(e.Message);

}

finally

{

//释放资源

if(fileStream!=null)

fileStream.Close();

}

实际上,BinryReader 和BinaryWriter 本身并不是独立的流,它们是对其他流的包装,所以创建BinryReader 和BinaryWriter 对象时,需要传给它们一个FileStream 对象,对BinryReader 和BinaryWriter 的操作,就是对FileStream 的操作。

C# 篇基础知识6——文件和流的更多相关文章

  1. python基础知识六 文件的基本操作+菜中菜

    基础知识六 文件操作 ​ open():打开 ​ file:文件的位置(路径) ​ mode:操作文件模式 ​ encoding:文件编码方式 ​ f :文件句柄 f = open("1.t ...

  2. Linux基础知识之文件的权限(一)

    Linux基础知识之文件权限(一) Linux优点之一就是它拥有多用户多任务的环境,在提供文件共享的同时也能保证用户文件的安全性.所以,设置文件的权限管理变得尤为重要. 权限讲解 [der@Der ~ ...

  3. Python进阶篇四:Python文件和流

    摘要: Python对于文件和流的操作与其他编程语言基本差不多,甚至语句上比其他语言更为简洁.文件和流函数针对的对象除了这两者之外还有,类文件(file-like),即python中只支持读却不支持写 ...

  4. Java基础知识强化之IO流笔记69:Properties练习之 判断文件中是否有指定的键,如果有就修改值的案例

    1. 我有一个文本文件(user.txt),我知道数据是键值对形式的,但是不知道内容是什么. 请写一个程序判断是否有"lisi"这样的键存在,如果有就改变其值为"100& ...

  5. Java基础知识强化之IO流笔记52:IO流练习之 把一个文件中的字符串排序后再写入另一个文件案例

    1. 把一个文件中的字符串排序后再写入另一个文件 已知s.txt文件中有这样的一个字符串:"hcexfgijkamdnoqrzstuvwybpl" 请编写程序读取数据内容,把数据排 ...

  6. Java基础知识强化之IO流笔记50:IO流练习之 复制多级文件夹的案例

    1. 复制多级文件夹的案例 需求:复制多级文件夹       数据源:E:\JavaSE\day21\code\demos     目的地:E:\   分析:         A:封装数据源File ...

  7. Java基础知识强化之IO流笔记49:IO流练习之 复制指定目录下指定后缀名的文件并修改名称的案例

    1. 复制指定目录下指定后缀名的文件并修改名称的案例     需求:复制指定目录下的指定文件,并修改后缀名.  • 指定的文件是:.java文件.     • 指定的后缀名是:.jad     • 指 ...

  8. Java基础知识强化之IO流笔记48:IO流练习之 复制单级文件夹案例

    1. 复制单级文件夹     数据源:e:\\demo     目的地:e:\\test 分析: A:封装目录 B:获取该目录下的所有文本的File数组 C:遍历该File数组,得到每一个File对象 ...

  9. Java基础知识强化之IO流笔记14:递归之输出指定目录下所有java文件绝对路径的案例

    1. 需求:输出指定目录下的所以.java结尾文件的绝对路径的案例:  分析:  A:封装目录  B:获取该目录下的所有文件和文件夹的File数组  C:遍历这个File数组,得到每一个File对象的 ...

随机推荐

  1. shell 脚本基础

    弱类型语言 bash 变量类型 本地变量 环境变量 局部变量 位置参数变量 特殊变量 运行 无执行权限 bash hello.sh 有执行权限 检查语法 bash -n user.sh 跟踪每一行的执 ...

  2. JS vue 组件创建过程

    https://www.jianshu.com/p/3504a1edba42 vue.js原生组件化开发(一)——组件开发基础 0.3472017.05.09 12:00:54字数 1120阅读 33 ...

  3. 2016-2017学年第三次测试赛 问题 F: 签到题

    问题 F: 签到题 时间限制: 1 Sec  内存限制: 128 MB提交: 80  解决: 28 提交统计讨论版 题目描述 在计算机网络考试中, 黑帅男神看到一个将IP网络分类的题, 精通C++的他 ...

  4. 基于Facebook开源框架SocketRocket的即时通讯

    SocketRocket 介绍: SocketRock 是 Facebook 开源的框架,基于 WebSocket 客户端类库,适用于 iOS.Mac OS.tv OS.GitHub 传送门:http ...

  5. video兼容ie,ckplayer网页播放器

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  6. Docker 之registry私有仓库搭建

    Docker 之registry私有仓库搭建 官方提供的私有仓库docker registry用法 https://yeasy.gitbooks.io/docker_practice/reposito ...

  7. robot framework 如何自己写模块下的方法或者库

    一.写模块(RF能识别的模块) 例如:F:\Python3.4\Lib\site-packages\robot\libraries这个库(包)下面的模块(.py),我们可以看下源码 注意:这种是以方法 ...

  8. 对象和Map转化gongju

    package czc.superzig.modular.utils; import java.lang.reflect.Field; import java.util.HashMap; import ...

  9. P1120/UVA307 小木棍(sticks) 题解

    题目描述 pdf 题解 注意的问题是,各个原始木棒的长度都是一样的! 说一下本题的总思路即:DFS+超强力剪枝!(详见本人的 AC 程序) 首先,我们要从小到大枚举原始木棒的长度len,也就是枚举答案 ...

  10. 63 滑动窗口的最大值 &&front(),back()操作前一定要判断容器的尺寸不能为0

    给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值.例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6, ...