一、什么是I/O
I/O 的全称是input/output,翻译过来就是输入/输出。对于一个系统或者计算机来说,键盘、U盘、网络接口、显示器、音响、摄像头等都是IO设备。
对于c#程序来说,I/O就是与外界进行数据交换的方式。程序需要对数据进行运算,I/O就是提供数据来源和输出数据的部分。
在C# 中,I/O体系整体分为三个部分,后台存储流、装饰器流、流适配器,具体划分如下图所示:
C#与Java不同,不区分字符流、字节流,采用Stream方式,针对Stream提供Reader与Writer的相关操作。
二、常用的c#I/O对象
1.Stream 基类
System.IO.Stream 是一个抽象类,提供了将字节(读,写等)传输到源的标准方法。
就像包装器类一样传输字节。需要从特定源读取/写入字节的类必须实现Stream类。
以下类继承Stream类
- FileStream:从物理文件读取字节或向物理文件写入字节,无论它是.txt,.exe,.jpg还是任何其他文件。FileStream派生自Stream类。
- MemoryStream: MemoryStream读取或写入存储在内存中的字节。
- BufferedStream: BufferedStream从其他Stream读取或写入字节,以提高某些I / O操作的性能。
- NetworkStream: NetworkStream从网络套接字读取或写入字节。
- PipeStream: PipeStream读取或写入来自不同进程的字节。
- CryptoStream: CryptoStream用于将数据流链接到密码转换。
使用方法:
FileStream:
FileStream
类为文件操作提供了一个流。它可以用于执行同步和异步的读写操作。FileStream
类的帮助下,我们可以轻松地将数据读写到文件中。写入:
using System; using System.IO; public class FileStreamExample { public static void Main(string[] args) { FileStream f = new FileStream("e:\\filestream-demo.txt", FileMode.OpenOrCreate);//creating file stream f.WriteByte(65);//writing byte into stream f.Close();//closing stream } }
using System; using System.IO; public class FileStreamExample { public static void Main(string[] args) { FileStream f = new FileStream("e:\\filestream-demo.txt", FileMode.OpenOrCreate); for (int i = 65; i <= 90; i++) { f.WriteByte((byte)i); } f.Close(); } }
using System; using System.IO; public class FileStreamExample { public static void Main(string[] args) { FileStream f = new FileStream("e:\\filestream-demo.txt", FileMode.OpenOrCreate); int i = 0; while ((i = f.ReadByte()) != -1) { Console.Write((char)i); } f.Close(); } }
MemoryStream:
MemoryStream是内存流,为系统内存提供读写操作,由于MemoryStream是通过无符号字节数组组成的
写入数据:
namespace MemoryStreamApp { class Program { static void Main(string[] args) { //构造MemoryStream实例,并输出初始分配容量及使用大小 MemoryStream mem = new MemoryStream(); Console.WriteLine("初始分配容量:{0}" , mem.Capacity); Console.WriteLine("初始使用量:{0}" , mem.Length); //将待写入的数据从字符串转换为字节数组 UnicodeEncoding encoder = new UnicodeEncoding(); byte[] bytes = encoder.GetBytes("新增数据"); //向内存流中写入数据 for (int i = 1; i < 4; i++) { Console.WriteLine("第{0}次写入新数据", i); mem.Write(bytes, 0, bytes.Length); } //写入数据后 MemoryStream 实例的容量和使用大小 Console.WriteLine("当前分配容量:{0}",mem.Capacity); Console.WriteLine("当前使用量:{0}",mem.Length); Console.ReadLine(); } } }
读取数据:
private void OnTestMemory() { //创建测试数据 CreateExampleData(); //创建内存流对象,初始分配50字节的缓冲区 MemoryStream mem = new MemoryStream(50); //向内存流中写入字节数组的所有数据 mem.Write(buffer,0,buffer.GetLength(0)); MessageBox.Show("写入数据后的内存流长度:" + mem.Length.ToString()); MessageBox.Show("分配给内存流的缓冲区大小:" + mem.Capacity.ToString()); mem.SetLength(550); MessageBox.Show("调用SetLength方法后的内存流长度:" + mem.Length.ToString()); mem.Capacity = 620;//此值不能小于Length属性 MessageBox.Show("调用Capacity方法后缓冲区大小:" + mem.Capacity.ToString()); //将读写指针移到距流开头10个字节的位置 mem.Seek(10,SeekOrigin.Begin); MessageBox.Show(mem.ReadByte().ToString()); mem.Close(); }
//内存流的Length属性代表了其中存放的数据的真实长度,
//而Capacity属性则代表了分配给内存流的内存空间大小。
//可以使用字节数组创建一个固定大小的MemoryStream,
// MemoryStream mem = new MemoryStream(buffer);
// 这时,无法再设置Capacity属性的大小。 还可以创建只读的内存流对象。
//MemoryStream mem = new MemoryStream(buffer,false);
BufferedStream:
BufferedStream能够实现流的缓存,对缓存中的数据进行写入或是读取
读写:
using System; using System.IO; using System.Text; class BufferedStreamTest { private static void AddText(BufferedStream bs, string value) { byte[]info = new UTF8Encoding().GetBytes(value); bs.Write(info, 0, info.Length); } public static void Main() { string path = "F:\\bsfile.txt"; if (File.Exists(path)) File.Delete(path); BufferedStream bs = new BufferedStream(File.Create(path)); AddText(bs, "The first line "); AddText(bs, "123456789\r\n"); AddText(bs, "The second line\r\n"); AddText(bs, "Another line"); bs.Close(); bs = new BufferedStream(File.OpenRead(path)); byte[]b = new byte[bs.Length]; UTF8Encoding utf8 = new UTF8Encoding(); while (bs.Read(b, 0, b.Length) > 0) Console.WriteLine(utf8.GetString(b)); Console.ReadKey(); } }
NetworkStream:
/// <summary> /// 服务端监听客户端信息,一旦有发送过来的信息,便立即处理 /// </summary> class Program { //全局TcpClient static TcpClient client; //文件流建立到磁盘上的读写流 static FileStream fs = new FileStream("E:\\abc.jpg", FileMode.Create); //buffer static int bufferlength = 200; static byte[] buffer = new byte[bufferlength]; //网络流 static NetworkStream ns; static void Main(string[] args) { ConnectAndListen(); } static void ConnectAndListen() { //服务端监听任何IP 但是端口号是80的连接 TcpListener listener = new TcpListener(IPAddress.Any,80); //监听对象开始监听 listener.Start(); while(true) { Console.WriteLine("等待连接"); //线程会挂在这里,直到客户端发来连接请求 client = listener.AcceptTcpClient(); Console.WriteLine("已经连接"); //得到从客户端传来的网络流 ns = client.GetStream(); //如果网络流中有数据 if (ns.DataAvailable) { //同步读取网络流中的byte信息 // do // { // ns.Read(buffer, 0, bufferlength); //} while (readLength > 0); //异步读取网络流中的byte信息 ns.BeginRead(buffer, 0, bufferlength, ReadAsyncCallBack, null); } } } /// <summary> /// 异步读取 /// </summary> /// <param name="result"></param> static void ReadAsyncCallBack(IAsyncResult result) { int readCount; //获得每次异步读取数量 readCount = client.GetStream().EndRead(result); //如果全部读完退出,垃圾回收 if (readCount < 1) { client.Close(); ns.Dispose(); fs.Dispose(); return; } //将网络流中的图片数据片段顺序写入本地 fs.Write(buffer, 0, 200); //再次异步读取 ns.BeginRead(buffer, 0, 200, ReadAsyncCallBack, null); } }
class Program { /// <summary> /// 客户端 /// </summary> /// <param name="args"></param> static void Main(string[] args) { SendImageToServer("xxx.jpg"); } static void SendImageToServer(string imgURl) { if (!File.Exists(imgURl)) return; //创建一个文件流打开图片 FileStream fs = File.Open(imgURl, FileMode.Open); //声明一个byte数组接受图片byte信息 byte[] fileBytes = new byte[fs.Length]; using (fs) { //将图片byte信息读入byte数组中 fs.Read(fileBytes, 0, fileBytes.Length); fs.Close(); } //找到服务器的IP地址 IPAddress address = IPAddress.Parse("127.0.0.1"); //创建TcpClient对象实现与服务器的连接 TcpClient client = new TcpClient(); //连接服务器 client.Connect(address, 80); using (client) { //连接完服务器后便在客户端和服务端之间产生一个流的通道 NetworkStream ns = client.GetStream(); using (ns) { //通过此通道将图片数据写入网络流,传向服务器端接收 ns.Write(fileBytes, 0, fileBytes.Length); } } } }
PipeStream:
PipeStream读取或写入来自不同进程的字节。
private static byte[] matchSign = {9, 0, 9, 0}; public static void Main() { string[] args = Environment.GetCommandLineArgs(); if (args.Length < 2) { Process clientProcess = new Process(); clientProcess.StartInfo.FileName = Environment.CommandLine; using (AnonymousPipeServerStream pipeServer = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable)) { // Pass the client process a handle to the server. clientProcess.StartInfo.Arguments = pipeServer.GetClientHandleAsString(); clientProcess.StartInfo.UseShellExecute = false; Console.WriteLine("[SERVER] Starting client process..."); clientProcess.Start(); pipeServer.DisposeLocalCopyOfClientHandle(); try { if (WaitForClientSign(pipeServer)) { Console.WriteLine("[SERVER] Valid sign code received!"); } else { Console.WriteLine("[SERVER] Invalid sign code received!"); } } catch (IOException e) { Console.WriteLine("[SERVER] Error: {0}", e.Message); } } clientProcess.WaitForExit(); clientProcess.Close(); Console.WriteLine("[SERVER] Client quit. Server terminating."); } else { using (PipeStream pipeClient = new AnonymousPipeClientStream(PipeDirection.Out, args[1])) { try { Console.WriteLine("[CLIENT] Sending sign code..."); SendClientSign(pipeClient); } catch (IOException e) { Console.WriteLine("[CLIENT] Error: {0}", e.Message); } } Console.WriteLine("[CLIENT] Terminating."); } } private static bool WaitForClientSign(PipeStream inStream) { byte[] inSign = new byte[matchSign.Length]; int len = inStream.Read(inSign, 0, matchSign.Length); bool valid = len == matchSign.Length; while (valid && len-- > 0) { valid = valid && (matchSign[len] == inSign[len]); } return valid; } private static void SendClientSign(PipeStream outStream) { outStream.Write(matchSign, 0, matchSign.Length); }
2.File类和Directory类
FIle类:
文件创建相关方法
- File.Create(String):在指定路径中创建或覆盖文件。
- File.Create(String, Int32):在指定路径中创建或覆盖文件,指定缓冲区大小。
- File.Create(String, Int32, FileOptions):创建或覆盖指定路径中的文件,指定缓冲区大小和一个描述如何创建或覆盖该文件的选项。
- File.CreateText(String) :创建或打开用于写入 UTF-8 编码文本的文件。 如果该文件已存在,将覆盖其内容。
- FileStream(String, FileMode, FileAccess, FileShare) :使用指定的路径、创建模式、读/写权限和共享权限创建 FileStream 类的新实例。
- FileStream(String, FileMode, FileAccess, FileShare, Int32, FileOptions) :使用指定的路径、创建模式、读/写和共享权限、其他FileStreams 可以具有的对此文件的访问权限、缓冲区大小和附加文件选项初始化 FileStream 类的新实例。
- FileStream(SafeFileHandle, FileAccess, Int32, Boolean):使用指定的读/写权限、缓冲区大小和同步或异步状态为指定的文件句柄初始化 FileStream 类的新实例。
- FileStream(String, FileMode, FileAccess):使用指定的路径、创建模式和读/写权限初始化 FileStream 类的新实例。
- Directory.CreateDirectory(String):在指定路径中创建所有目录和子目录,除非它们已经存在。
文件删除相关方法
- File.Delete(String):删除指定的文件。
- Directory.Delete(String):从指定路径删除空目录。
- Directory.Delete(String, Boolean):删除指定的目录,并删除该目录中的所有子目录和文件(如果表示)。
文件操作判断相关方法
-
File.Exists(String):确定指定的文件是否存在。
- Directory.Exists(String) :确定给定路径是否引用磁盘上的现有目录。
示例代码:
using System; using System.IO; class Test { public static void Main() { string path = @"c:\temp\MyTest.txt"; if (!File.Exists(path)) { // 创建一个要写入的文件。 using (StreamWriter sw = File.CreateText(path)) { sw.WriteLine("Hello"); sw.WriteLine("And"); sw.WriteLine("Welcome"); } } // 打开要从中读取的文件。 using (StreamReader sr = File.OpenText(path)) { string s; while ((s = sr.ReadLine()) != null) { Console.WriteLine(s); } } } }
Directory类
- 1、CreateDirectory 根据指定路径创建目录。有重载,允许一次过创建多个目录。
- 2、Delete 删除指定的目录。 有重载,指示目录有子目录的情况下,是否删除子目录。 true则连同子目录一起删除。flase则不删除目录,并返回 一个异常。
- 3、Exists 确定给定路径是否引用磁盘上的现有目录。
- 4、GetAccessControl 已重载。 返回某个目录的 Windows 访问控制列表 (ACL)。
- 5、GetCreationTime 获取目录的创建日期和时间。
- 6、GetCreationTimeUtc 获取目录创建的日期和时间,其格式为协调通用时间 (UTC)。
- 7、GetCurrentDirectory 获取应用程序的当前工作目录。
- 8、GetDirectories 已重载。 获取指定目录中子目录的名称(字符串数组)。支持用正则表达式匹配符合名称的目录名。 注意,只返回目录名,不返回文件名
- 9、GetDirectoryRoot 返回指定路径的卷信息、根信息或两者同时返回。
- 10、GetFiles 已重载。 返回指定目录中的文件的名称(字符串数组)。注意只返回文件名,不返回目录
- 11、GetFileSystemEntries 已重载。 返回指定目录中所有文件和子目录的名称(字符串数组)。目录名与文件名一起返回。 支持正则表达式检索。
- 12、GetLastAccessTime 返回上次访问指定文件或目录的日期和时间。
- 13、GetLastAccessTimeUtc 返回上次访问指定文件或目录的日期和时间,其格式为协调通用时间 (UTC)。
- 14、GetLastWriteTime 返回上次写入指定文件或目录的日期和时间。
- 15、GetLastWriteTimeUtc 返回上次写入指定文件或目录的日期和时间,其格式为协调通用时间 (UTC)。
- 16、GetLogicalDrives 检索此计算机上格式为“<驱动器号>:\”的逻辑驱动器的名称。
- 17、GetParent 检索指定路径的父目录,包括绝对路径和相对路径。
- 18、Move 将文件或目录及其内容移到新位置。
- 19、SetAccessControl 将 DirectorySecurity 对象描述的访问控制列表 (ACL) 项应用于指定的目录。
- 20、SetCreationTime 为指定的文件或目录设置创建日期和时间。
- 21、SetCreationTimeUtc 设置指定文件或目录的创建日期和时间,其格式为协调通用时间 (UTC)。
- 22、SetCurrentDirectory 将应用程序的当前工作目录设置为指定的目录。
- 23、SetLastAccessTime 设置上次访问指定文件或目录的日期和时间。
- 24、SetLastAccessTimeUtc 设置上次访问指定文件或目录的日期和时间,其格式为协调通用时间 (UTC)。
- 25、SetLastWriteTime 设置上次写入目录的日期和时间。
- 26、SetLastWriteTimeUtc 设置上次写入某个目录的日期和时间,其格式为协调通用时间 (UTC)。
示例:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.IO; namespace Windows { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { //1、是否存在文件夹 string aaaa = "H:\\NCRE"; //路径的正确写法 if (Directory.Exists(aaaa)) //如果不存在就创建file文件夹 { MessageBox.Show("存在文件夹!"); } else { MessageBox.Show("不存在文件夹!"); } } private void button2_Click(object sender, EventArgs e) { //5、文件夹是否已经删除 string aaaa = "H:\\1"; //路径的正确写法 if (Directory.Exists(aaaa)) //判断是否存在 { MessageBox.Show("文件夹还在,没有删除!"); } else { MessageBox.Show("文件夹已经删除!"); } } private void button3_Click(object sender, EventArgs e) {//6、从a移动到b,判断a没有了,b有了 string aaaa = "D:\\NCRE"; //D盘是之前的路径 string bbbb = "H:\\NCRE";//H盘是移动之后的路径 if (!Directory.Exists(aaaa) & Directory.Exists(bbbb)) //如果没有在D,在H证明成功 { MessageBox.Show("D盘没有,H盘有!移动成功!"); } else { MessageBox.Show("移动失败!"); } } } }
3.Path类
常用方法
- Path.GetFullPath(file) 取全路径
- Path.GetFileName(file) 取文件名,包含扩展名
- Path.GetFileNameWithoutExtension(file) 取文件名,不包含扩展名
- Path.GetExtension(file) 取扩展名
- Path.GetDirectoryName(file) 取路径名
- Path.GetPathRoot(file) 取盘符
- Path.Combine(file1,file2) 合并2个路径
示例:
string str = @"C:UsersAdministratorDesktopceshi.txt"; //获得文件名 Console.WriteLine(Path.GetFileName(str)); //获得不包含扩展名的文件名 Console.WriteLine(Path.GetFileNameWithoutExtension(str)); //获得文件所在文件夹的名称 Console.WriteLine(Path.GetDirectoryName(str)); //获得文件所在的全路径 Console.WriteLine(Path.GetFullPath(str)); //拼接路径字符串 Console.WriteLine(Path.Combine(@"D:ab","c.txt")); Console.ReadKey();
属性或方法
- string ChangeExtension(string path, string extension) 更改路径字符串的扩展名
- string Combine(params string[] paths) 将字符串数组组合成一个路径
- string Combine(string path1, string path2) 将两个字符串组合成一个路径
- string GetDirectoryName(string path) 返回指定路径字符串的目录信息
- string GetExtension(string path) 返回指定路径字符串的扩展名
- string GetFileName(string path) 返回指定路径字符串的文件名和扩展名
- string GetFileNameWithoutExtension(string path) 返回不具有扩展名的指定路径字符串的文件名
- string GetFullPath(string path) 返回指定路径字符串的绝对路径
- char[] GetInvalidFileNameChars() 获取包含不允许在文件名中使用的字符的数组
- char[] GetInvalidPathChars() 获取包含不允许在路径名中使用的字符的数组
- string GetPathRoot(string path) 获取指定路径的根目录信息
- string GetRandomFileName() 返回随机文件夹名或文件名
- string GetTempPath() 返回当前用户的临时文件夹的路径
- bool HasExtension(string path) 返回路径是否包含文件的扩展名
- bool IsPathRooted(string path) 返回路径字符串是否包含根
5.StreamReader(快速读取文本文件)
StreamReader对象的创建方式非常类似于StreamWriter对象。创建它的最常见方式是使用前面创建的FileStream对象:
FileStream fs = new FileStream("test.txt",FileMode.Open);
StreamReader sr = new StreamReader(fs);
同StreamWriter一样,StreamReader类可以直接在包含具体文件路径的字符串中创建:
StreamReader sr = new StreamReader("test.txt");
示例:
using System; using System.Collections.Generic; using System.Text; using System.IO; class Program { static void Main(string[]args) { try { FileStream aFile = new FileStream(@"c:\祝福.txt", FileMode.Open); StreamReader sr = new StreamReader(aFile); string strLine = sr.ReadLine(); while(strLine != null) { Console.WriteLine(strLine); strLine = sr.ReadLine(); } sr.Close(); } catch (IOException ex) { Console.WriteLine(ex.Message); Console.ReadLine(); return ; } Console.ReadKey(); } }
6.StreamWriter(快速写入文本文件)
除了使用FileStream类读写文本文件,.net还提供了StreamWriter类和StreamReader类专门处理文本文件。
这两个类从底层封装了文件流,读写时不用重新编码,提供了更文件的读写方式。
StreamWriter类允许将字符和字符串写入到文件中,不必转换为字节,它处理底层的转换,向FileStream对象写入数据。
如果已经有了FileStream对象,则可以使用此对象来创建StreamWriter对象:
FileStream fs = new FileStream("test.txt",FileMode.CreateNew);
StreamWriter sw = new StreamWriter(fs);
也可以直接从文件中创建StreamWriter对象:
StreamWriter sw = new StreamWriter("test.txt",true);
此构造函数中有两个参数,一个是文件名,另一个是布尔值,这个布尔值规定创建对象的方式如下:
如果此值为false,则创建一个新文件,如果存在原文件,则覆盖。
如果此值为true,则打开文件保留原来数据,如果找不到文件,则创建新文件。
与 创建FileStream对象不同,创建StreamWriter对象不会提供一组类似的选项:
除了使用Boolean值添加到文件的末尾或创建新文件之外,
根本没有像FileStream类那样指定FileMode属性的选项。
而且,没有设置FileAccess属性的选项,因此总是有对文件的读/写权限。
为了使用高级参数,必须先在FileStream构造函数中指定这些参数,然后在FileStream对象中创建StreamWriter。
示例:
using System; using System.Collections.Generic; using System.Text; using System.IO; class Program { static void Main(string[]args) { try { FileStream aFile = new FileStream(@"c:\123\欢迎.txt", FileMode.OpenOrCreate); StreamWriter sw = new StreamWriter(aFile); sw.WriteLine("为今后我们之间的进一步合作,"); sw.WriteLine("为我们之间日益增进的友谊,"); sw.Write("为朋友们的健康幸福,"); sw.Write("干杯!朋友!"); sw.Close(); } catch (IOException ex) { Console.WriteLine(ex.Message); Console.ReadLine(); return ; } } }