C# 篇基础知识6——文件和流
计算机以文件的形式把数据存储在磁盘、光盘等存储设备上。文件的管理和操作是操作系统的一个重要组成部分,.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——文件和流的更多相关文章
- python基础知识六 文件的基本操作+菜中菜
基础知识六 文件操作 open():打开 file:文件的位置(路径) mode:操作文件模式 encoding:文件编码方式 f :文件句柄 f = open("1.t ...
- Linux基础知识之文件的权限(一)
Linux基础知识之文件权限(一) Linux优点之一就是它拥有多用户多任务的环境,在提供文件共享的同时也能保证用户文件的安全性.所以,设置文件的权限管理变得尤为重要. 权限讲解 [der@Der ~ ...
- Python进阶篇四:Python文件和流
摘要: Python对于文件和流的操作与其他编程语言基本差不多,甚至语句上比其他语言更为简洁.文件和流函数针对的对象除了这两者之外还有,类文件(file-like),即python中只支持读却不支持写 ...
- Java基础知识强化之IO流笔记69:Properties练习之 判断文件中是否有指定的键,如果有就修改值的案例
1. 我有一个文本文件(user.txt),我知道数据是键值对形式的,但是不知道内容是什么. 请写一个程序判断是否有"lisi"这样的键存在,如果有就改变其值为"100& ...
- Java基础知识强化之IO流笔记52:IO流练习之 把一个文件中的字符串排序后再写入另一个文件案例
1. 把一个文件中的字符串排序后再写入另一个文件 已知s.txt文件中有这样的一个字符串:"hcexfgijkamdnoqrzstuvwybpl" 请编写程序读取数据内容,把数据排 ...
- Java基础知识强化之IO流笔记50:IO流练习之 复制多级文件夹的案例
1. 复制多级文件夹的案例 需求:复制多级文件夹 数据源:E:\JavaSE\day21\code\demos 目的地:E:\ 分析: A:封装数据源File ...
- Java基础知识强化之IO流笔记49:IO流练习之 复制指定目录下指定后缀名的文件并修改名称的案例
1. 复制指定目录下指定后缀名的文件并修改名称的案例 需求:复制指定目录下的指定文件,并修改后缀名. • 指定的文件是:.java文件. • 指定的后缀名是:.jad • 指 ...
- Java基础知识强化之IO流笔记48:IO流练习之 复制单级文件夹案例
1. 复制单级文件夹 数据源:e:\\demo 目的地:e:\\test 分析: A:封装目录 B:获取该目录下的所有文本的File数组 C:遍历该File数组,得到每一个File对象 ...
- Java基础知识强化之IO流笔记14:递归之输出指定目录下所有java文件绝对路径的案例
1. 需求:输出指定目录下的所以.java结尾文件的绝对路径的案例: 分析: A:封装目录 B:获取该目录下的所有文件和文件夹的File数组 C:遍历这个File数组,得到每一个File对象的 ...
随机推荐
- shell 脚本基础
弱类型语言 bash 变量类型 本地变量 环境变量 局部变量 位置参数变量 特殊变量 运行 无执行权限 bash hello.sh 有执行权限 检查语法 bash -n user.sh 跟踪每一行的执 ...
- JS vue 组件创建过程
https://www.jianshu.com/p/3504a1edba42 vue.js原生组件化开发(一)——组件开发基础 0.3472017.05.09 12:00:54字数 1120阅读 33 ...
- 2016-2017学年第三次测试赛 问题 F: 签到题
问题 F: 签到题 时间限制: 1 Sec 内存限制: 128 MB提交: 80 解决: 28 提交统计讨论版 题目描述 在计算机网络考试中, 黑帅男神看到一个将IP网络分类的题, 精通C++的他 ...
- 基于Facebook开源框架SocketRocket的即时通讯
SocketRocket 介绍: SocketRock 是 Facebook 开源的框架,基于 WebSocket 客户端类库,适用于 iOS.Mac OS.tv OS.GitHub 传送门:http ...
- video兼容ie,ckplayer网页播放器
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- Docker 之registry私有仓库搭建
Docker 之registry私有仓库搭建 官方提供的私有仓库docker registry用法 https://yeasy.gitbooks.io/docker_practice/reposito ...
- robot framework 如何自己写模块下的方法或者库
一.写模块(RF能识别的模块) 例如:F:\Python3.4\Lib\site-packages\robot\libraries这个库(包)下面的模块(.py),我们可以看下源码 注意:这种是以方法 ...
- 对象和Map转化gongju
package czc.superzig.modular.utils; import java.lang.reflect.Field; import java.util.HashMap; import ...
- P1120/UVA307 小木棍(sticks) 题解
题目描述 pdf 题解 注意的问题是,各个原始木棒的长度都是一样的! 说一下本题的总思路即:DFS+超强力剪枝!(详见本人的 AC 程序) 首先,我们要从小到大枚举原始木棒的长度len,也就是枚举答案 ...
- 63 滑动窗口的最大值 &&front(),back()操作前一定要判断容器的尺寸不能为0
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值.例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6, ...