I/O(输入/输出)操作是计算机程序中不可或缺的一部分,无论是读取文件、写入数据,还是处理网络通信,I/O都发挥着重要作用。C#作为一种现代化的编程语言,提供了丰富的I/O操作支持,包括文件I/O、流操作、网络I/O等。本文将深入探讨C#中的流与I/O,从基本概念到高级用法,全面解析I/O操作的原理和机制,并结合实际案例,帮助读者掌握I/O编程的精髓。
I/O操作的基本概念
I/O设备
I/O设备是计算机系统中用于输入和输出数据的设备,包括键盘、鼠标、显示器、打印机、磁盘驱动器、网络接口等。I/O设备通过I/O端口与计算机系统进行通信,实现数据的输入和输出。
I/O操作类型
I/O操作可以分为两种类型:同步I/O和异步I/O。
- 同步I/O:同步I/O操作在执行过程中会阻塞调用线程,直到I/O操作完成。这种方式简单易用,但可能导致性能问题,特别是在高并发场景下。
 - 异步I/O:异步I/O操作在执行过程中不会阻塞调用线程,可以通过回调、事件或任务的方式在操作完成后通知调用者。这种方式可以提高系统的并发性能,但编程相对复杂。
 
流的基本概念
什么是流
流(Stream)是一个抽象的概念,用于表示数据的有序序列。流可以是字节流或字符流,前者用于处理二进制数据,后者用于处理文本数据。C#中的流通过System.IO.Stream类及其派生类实现。
流的分类
根据数据流动的方向,流可以分为输入流和输出流。输入流用于从数据源读取数据,输出流用于向数据目标写入数据。
流的基本操作
流的基本操作包括读取数据、写入数据、关闭流等。C#中的Stream类及其派生类提供了这些操作的基本方法。
文件I/O操作
文件流
文件流(FileStream)是用于文件读写操作的流,通过System.IO.FileStream类实现。FileStream提供了对文件进行读写操作的基础支持。
using System;using System.IO;public class Program{public static void Main(string[] args){string filePath = "example.txt";// 写入文件using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)){byte[] data = System.Text.Encoding.UTF8.GetBytes("Hello, FileStream!");fileStream.Write(data, 0, data.Length);}// 读取文件using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)){byte[] data = new byte[fileStream.Length];fileStream.Read(data, 0, data.Length);string content = System.Text.Encoding.UTF8.GetString(data);Console.WriteLine(content);}}}
在这个例子中,我们使用FileStream类分别进行文件的写入和读取操作。
文件读写快捷方法
C#中的System.IO.File类提供了一些方便的方法,用于快速进行文件的读写操作。
using System;using System.IO;public class Program{public static void Main(string[] args){string filePath = "example.txt";// 写入文件File.WriteAllText(filePath, "Hello, File!");// 读取文件string content = File.ReadAllText(filePath);Console.WriteLine(content);}}
在这个例子中,我们使用File.WriteAllText和File.ReadAllText方法快速进行文件的写入和读取操作。
字符流操作
StreamReader和StreamWriter
StreamReader和StreamWriter是用于读取和写入字符流的类,通过System.IO.StreamReader和System.IO.StreamWriter类实现。
using System;using System.IO;public class Program{public static void Main(string[] args){string filePath = "example.txt";// 写入文件using (StreamWriter writer = new StreamWriter(filePath)){writer.WriteLine("Hello, StreamWriter!");}// 读取文件using (StreamReader reader = new StreamReader(filePath)){string content = reader.ReadToEnd();Console.WriteLine(content);}}}
在这个例子中,我们使用StreamWriter和StreamReader类分别进行文件的写入和读取操作。
StringReader和StringWriter
StringReader和StringWriter是用于读取和写入字符串的类,通过System.IO.StringReader和System.IO.StringWriter类实现。
using System;using System.IO;public class Program{public static void Main(string[] args){string input = "Hello, StringReader!";string output;// 写入字符串using (StringWriter writer = new StringWriter()){writer.WriteLine(input);output = writer.ToString();}// 读取字符串using (StringReader reader = new StringReader(output)){string content = reader.ReadToEnd();Console.WriteLine(content);}}}
在这个例子中,我们使用StringWriter和StringReader类分别进行字符串的写入和读取操作。
缓冲流操作
BufferedStream
BufferedStream是用于提高I/O操作性能的缓冲流,通过System.IO.BufferedStream类实现。缓冲流通过减少对底层设备的直接访问次数来提高性能。
using System;using System.IO;public class Program{public static void Main(string[] args){string filePath = "example.txt";// 写入文件using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))using (BufferedStream bufferedStream = new BufferedStream(fileStream)){byte[] data = System.Text.Encoding.UTF8.GetBytes("Hello, BufferedStream!");bufferedStream.Write(data, 0, data.Length);}// 读取文件using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))using (BufferedStream bufferedStream = new BufferedStream(fileStream)){byte[] data = new byte[bufferedStream.Length];bufferedStream.Read(data, 0, data.Length);string content = System.Text.Encoding.UTF8.GetString(data);Console.WriteLine(content);}}}
在这个例子中,我们使用BufferedStream类分别进行文件的写入和读取操作。
内存流操作
MemoryStream
MemoryStream是用于在内存中读写数据的流,通过System.IO.MemoryStream类实现。内存流适用于需要高效处理小数据量的场景。
using System;using System.IO;public class Program{public static void Main(string[] args){byte[] buffer;// 写入内存流using (MemoryStream memoryStream = new MemoryStream()){byte[] data = System.Text.Encoding.UTF8.GetBytes("Hello, MemoryStream!");memoryStream.Write(data, 0, data.Length);buffer = memoryStream.ToArray();}// 读取内存流using (MemoryStream memoryStream = new MemoryStream(buffer)){byte[] data = new byte[memoryStream.Length];memoryStream.Read(data, 0, data.Length);string content = System.Text.Encoding.UTF8.GetString(data);Console.WriteLine(content);}}}
在这个例子中,我们使用MemoryStream类分别进行内存数据的写入和读取操作。
压缩流操作
GZipStream和DeflateStream
GZipStream和DeflateStream是用于数据压缩和解压缩的流,通过System.IO.Compression.GZipStream和System.IO.Compression.DeflateStream类实现。
using System;using System.IO;using System.IO.Compression;public class Program{public static void Main(string[] args){string filePath = "example.txt";string compressedFilePath = "example.gz";// 写入文件File.WriteAllText(filePath, "Hello, GZipStream!");// 压缩文件using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))using (FileStream compressedFileStream = new FileStream(compressedFilePath, FileMode.Create, FileAccess.Write))using (GZipStream gzipStream = new GZipStream(compressedFileStream, CompressionMode.Compress)){fileStream.CopyTo(gzipStream);}// 解压文件using (FileStream compressedFileStream = new FileStream(compressedFilePath, FileMode.Open, FileAccess.Read))using (GZipStream gzipStream = new GZipStream(compressedFileStream, CompressionMode.Decompress))using (StreamReader reader = new StreamReader(gzipStream)){string content = reader.ReadToEnd();Console.WriteLine(content);}}}
在这个例子中,我们使用GZipStream类分别进行文件的压缩和解压操作。
网络I/O操作
TcpClient和TcpListener
TcpClient和TcpListener是用于TCP网络通信的类,通过System.Net.Sockets.TcpClient和System.Net.Sockets.TcpListener类实现。
using System;using System.Net;using System.Net.Sockets;using System.Text;using System.Threading.Tasks;public class Program{public static async Task Main(string[] args){int port = 12345;Task serverTask = StartServerAsync(port);Task clientTask = StartClientAsync(port);await Task.WhenAll(serverTask, clientTask);}public static async Task StartServerAsync(int port){TcpListener listener = new TcpListener(IPAddress.Any, port);listener.Start();Console.WriteLine("服务器已启动");while (true){TcpClient client = await listener.AcceptTcpClientAsync();_ = HandleClientAsync(client);}}public static async Task HandleClientAsync(TcpClient client){NetworkStream stream = client.GetStream();byte[] buffer = new byte[1024];int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);Console.WriteLine($"收到消息:{message}");string response = "Hello from server!";byte[] responseBytes = Encoding.UTF8.GetBytes(response);await stream.WriteAsync(responseBytes, 0, responseBytes.Length);client.Close();}public static async Task StartClientAsync(int port){await Task.Delay(1000); // 延迟以确保服务器已启动TcpClient client = new TcpClient();await client.ConnectAsync("127.0.0.1", port);NetworkStream stream = client.GetStream();string message = "Hello from client!";byte[] messageBytes = Encoding.UTF8.GetBytes(message);await stream.WriteAsync(messageBytes, 0, messageBytes.Length);byte[] buffer = new byte[1024];int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);string response = Encoding.UTF8.GetString(buffer, 0, bytesRead);Console.WriteLine($"收到响应:{response}");client.Close();}}
在这个例子中,我们使用TcpClient和TcpListener类实现了一个简单的TCP网络通信示例。
HttpClient
HttpClient是用于HTTP网络通信的类,通过System.Net.Http.HttpClient类实现。
using System;using System.Net.Http;using System.Threading.Tasks;public class Program{public static async Task Main(string[] args){string url = "https://api.github.com/repos/dotnet/roslyn";HttpClient client = new HttpClient();client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (compatible; AcmeInc/1.0)");HttpResponseMessage response = await client.GetAsync(url);response.EnsureSuccessStatusCode();string content = await response.Content.ReadAsStringAsync();Console.WriteLine(content);}}
在这个例子中,我们使用HttpClient类进行HTTP GET请求,并输出响应内容。
异步I/O操作
异步文件I/O
通过使用异步方法,可以提高文件I/O操作的性能。System.IO.File类提供了多种异步方法。
using System;using System.IO;using System.Text;using System.Threading.Tasks;public class Program{public static async Task Main(string[] args){string filePath = "example.txt";// 写入文件string content = "Hello, Async File I/O!";byte[] data = Encoding.UTF8.GetBytes(content);await File.WriteAllBytesAsync(filePath, data);// 读取文件byte[] readData = await File.ReadAllBytesAsync(filePath);string readContent = Encoding.UTF8.GetString(readData);Console.WriteLine(readContent);}}
在这个例子中,我们使用异步方法分别进行文件的写入和读取操作。
异步流操作
通过使用异步方法,可以提高流操作的性能。System.IO.Stream类及其派生类提供了多种异步方法。
using System;using System.IO;using System.Text;using System.Threading.Tasks;public class Program{public static async Task Main(string[] args){string filePath = "example.txt";// 写入文件using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)){byte[] data = Encoding.UTF8.GetBytes("Hello, Async FileStream!");await fileStream.WriteAsync(data, 0, data.Length);}// 读取文件using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)){byte[] data = new byte[fileStream.Length];await fileStream.ReadAsync(data, 0, data.Length);string content = Encoding.UTF8.GetString(data);Console.WriteLine(content);}}}
在这个例子中,我们使用异步方法分别进行文件流的写入和读取操作。
高级I/O操作
文件监视
通过System.IO.FileSystemWatcher类,可以监视文件系统的变化,如文件创建、删除、修改等。
using System;using System.IO;public class Program{public static void Main(string[] args){string path = ".";using (FileSystemWatcher watcher = new FileSystemWatcher()){watcher.Path = path;watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Size;watcher.Filter = "*.txt";watcher.Created += OnChanged;watcher.Changed += OnChanged;watcher.Deleted += OnChanged;watcher.Renamed += OnRenamed;watcher.EnableRaisingEvents = true;Console.WriteLine($"监视文件夹:{path}");Console.WriteLine("按回车键退出...");Console.ReadLine();}}private static void OnChanged(object source, FileSystemEventArgs e){Console.WriteLine($"文件 {e.ChangeType}: {e.FullPath}");}private static void OnRenamed(object source, RenamedEventArgs e){Console.WriteLine($"文件重命名: {e.OldFullPath} 改为 {e.FullPath}");}}
在这个例子中,我们使用FileSystemWatcher类监视当前文件夹中的文本文件变化,并输出变化信息。
临时文件和文件夹
通过System.IO.Path类和System.IO.File类,可以创建和使用临时文件和文件夹。
using System;using System.IO;public class Program{public static void Main(string[] args){string tempFile = Path.GetTempFileName();string tempFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());// 创建临时文件File.WriteAllText(tempFile, "Hello, Temp File!");// 创建临时文件夹Directory.CreateDirectory(tempFolder);// 输出临时文件和文件夹路径Console.WriteLine($"临时文件:{tempFile}");Console.WriteLine($"临时文件夹:{tempFolder}");// 删除临时文件和文件夹File.Delete(tempFile);Directory.Delete(tempFolder);Console.WriteLine("临时文件和文件夹已删除");}}
在这个例子中,我们使用Path.GetTempFileName和Path.GetTempPath方法创建临时文件和文件夹,并在操作完成后删除它们。
文件和目录操作
通过System.IO.File类和System.IO.Directory类,可以进行文件和目录的各种操作,如复制、移动、删除等。
using System;using System.IO;public class Program{public static void Main(string[] args){string sourceFile = "source.txt";string destFile = "dest.txt";string sourceDir = "sourceDir";string destDir = "destDir";// 创建文件和目录File.WriteAllText(sourceFile, "Hello, File!");Directory.CreateDirectory(sourceDir);// 复制文件和目录File.Copy(sourceFile, destFile, true);DirectoryCopy(sourceDir, destDir, true);// 移动文件和目录File.Move(destFile, "moved.txt");Directory.Move(destDir, "movedDir");// 删除文件和目录File.Delete("moved.txt");Directory.Delete("movedDir", true);Console.WriteLine("文件和目录操作完成");}private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs){DirectoryInfo dir = new DirectoryInfo(sourceDirName);DirectoryInfo[] dirs = dir.GetDirectories();Directory.CreateDirectory(destDirName);FileInfo[] files = dir.GetFiles();foreach (FileInfo file in files){string tempPath = Path.Combine(destDirName, file.Name);file.CopyTo(tempPath, false);}if (copySubDirs){foreach (DirectoryInfo subdir in dirs){string tempPath = Path.Combine(destDirName, subdir.Name);DirectoryCopy(subdir.FullName, tempPath, copySubDirs);}}}}
在这个例子中,我们使用File类和Directory类进行文件和目录的复制、移动和删除操作。
I/O性能优化
缓冲和批量操作
通过使用缓冲和批量操作,可以减少I/O操作的次数,从而提高性能。BufferedStream类和批量读取/写入方法是实现缓冲和批量操作的常用方法。
using System;using System.IO;public class Program{public static void Main(string[] args){string filePath = "example.txt";// 批量写入文件using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))using (BufferedStream bufferedStream = new BufferedStream(fileStream)){for (int i = 0; i < 10000; i++){byte[] data = System.Text.Encoding.UTF8.GetBytes("Hello, BufferedStream!\n");bufferedStream.Write(data, 0, data.Length);}}// 批量读取文件using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))using (BufferedStream bufferedStream = new BufferedStream(fileStream))using (StreamReader reader = new StreamReader(bufferedStream)){while (!reader.EndOfStream){string line = reader.ReadLine();// 处理读取的数据}}Console.WriteLine("文件读写完成");}}
在这个例子中,我们使用BufferedStream类进行批量写入和读取操作,以提高I/O性能。
异步I/O
通过使用异步I/O操作,可以避免阻塞调用线程,从而提高系统的并发性能。System.IO.File类和System.IO.Stream类及其派生类提供了多种异步方法。
using System;using System.IO;using System.Text;using System.Threading.Tasks;public class Program{public static async Task Main(string[] args){string filePath = "example.txt";// 异步写入文件string content = "Hello, Async File I/O!";byte[] data = Encoding.UTF8.GetBytes(content);await File.WriteAllBytesAsync(filePath, data);// 异步读取文件byte[] readData = await File.ReadAllBytesAsync(filePath);string readContent = Encoding.UTF8.GetString(readData);Console.WriteLine(readContent);}}
在这个例子中,我们使用异步方法分别进行文件的写入和读取操作,以提高I/O性能。
I/O错误处理
异常处理
在进行I/O操作时,可能会遇到各种错误情况,如文件未找到、访问权限不足、磁盘空间不足等。通过异常处理,可以捕获和处理这些错误情况。
using System;using System.IO;public class Program{public static void Main(string[] args){string filePath = "example.txt";try{// 写入文件File.WriteAllText(filePath, "Hello, File!");// 读取文件string content = File.ReadAllText(filePath);Console.WriteLine(content);}catch (FileNotFoundException ex){Console.WriteLine($"文件未找到:{ex.Message}");}catch (UnauthorizedAccessException ex){Console.WriteLine($"访问权限不足:{ex.Message}");}catch (IOException ex){Console.WriteLine($"I/O错误:{ex.Message}");}catch (Exception ex){Console.WriteLine($"其他错误:{ex.Message}");}}}
在这个例子中,我们通过异常处理捕获并处理了文件操作中的各种错误情况。
日志记录
在进行I/O操作时,通过记录日志,可以保存错误信息和操作记录,方便后续分析和排查问题。
using System;using System.IO;public class Program{public static void Main(string[] args){string filePath = "example.txt";try{// 写入文件File.WriteAllText(filePath, "Hello, File!");// 读取文件string content = File.ReadAllText(filePath);Console.WriteLine(content);}catch (Exception ex){LogError(ex);}}private static void LogError(Exception ex){string logFilePath = "error.log";string logMessage = $"{DateTime.Now}: {ex.Message}{Environment.NewLine}";File.AppendAllText(logFilePath, logMessage);}}
在这个例子中,我们通过记录日志保存了文件操作中的错误信息。
I/O的安全性
文件访问权限
在进行文件操作时,需要注意文件的访问权限。通过设置文件访问权限,可以限制对文件的读写操作,确保文件的安全性。
using System;using System.IO;using System.Security.AccessControl;public class Program{public static void Main(string[] args){string filePath = "example.txt";// 创建文件并设置访问权限File.WriteAllText(filePath, "Hello, Secure File!");FileSecurity fileSecurity = new FileSecurity();fileSecurity.AddAccessRule(new FileSystemAccessRule("Everyone", FileSystemRights.Read, AccessControlType.Allow));File.SetAccessControl(filePath, fileSecurity);// 读取文件string content = File.ReadAllText(filePath);Console.WriteLine(content);}}
在这个例子中,我们通过设置文件访问权限,限制了对文件的读写操作。
文件加密
在进行文件操作时,通过文件加密,可以保护文件内容的机密性,确保文件的安全性。
using System;using System.IO;using System.Security.Cryptography;using System.Text;public class Program{public static void Main(string[] args){string filePath = "example.txt";string encryptedFilePath = "example.encrypted";string decryptedFilePath = "example.decrypted";string content = "Hello, Secure File!";string password = "password";// 加密文件EncryptFile(filePath, encryptedFilePath, password);// 解密文件DecryptFile(encryptedFilePath, decryptedFilePath, password);// 读取解密后的文件string decryptedContent = File.ReadAllText(decryptedFilePath);Console.WriteLine(decryptedContent);}private static void EncryptFile(string inputFile, string outputFile, string password){byte[] salt = GenerateSalt();using (Aes aes = Aes.Create()){using (Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt)){aes.Key = key.GetBytes(aes.KeySize / 8);aes.IV = key.GetBytes(aes.BlockSize / 8);using (FileStream outputStream = new FileStream(outputFile, FileMode.Create)){outputStream.Write(salt, 0, salt.Length);using (CryptoStream cryptoStream = new CryptoStream(outputStream, aes.CreateEncryptor(), CryptoStreamMode.Write)){using (FileStream inputStream = new FileStream(inputFile, FileMode.Open)){inputStream.CopyTo(cryptoStream);}}}}}}private static void DecryptFile(string inputFile, string outputFile, string password){byte[] salt = new byte[16];using (FileStream inputStream = new FileStream(inputFile, FileMode.Open)){inputStream.Read(salt, 0, salt.Length);using (Aes aes = Aes.Create()){using (Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt)){aes.Key = key.GetBytes(aes.KeySize / 8);aes.IV = key.GetBytes(aes.BlockSize / 8);using (CryptoStream cryptoStream = new CryptoStream(inputStream, aes.CreateDecryptor(), CryptoStreamMode.Read)){using (FileStream outputStream = new FileStream(outputFile, FileMode.Create)){cryptoStream.CopyTo(outputStream);}}}}}}private static byte[] GenerateSalt(){byte[] salt = new byte[16];using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()){rng.GetBytes(salt);}return salt;}}
在这个例子中,我们通过文件加密和解密方法,保护了文件内容的机密性。
小结
I/O操作是C#编程中的一个重要方面,通过文件I/O、流操作、网络I/O等功能,开发者可以高效地处理各种数据读写和通信任务。本文深入探讨了C#中的流与I/O,从基本概念到高级用法,全面解析了I/O操作的原理和机制,并结合实际案例展示了I/O操作在文件操作、字符流操作、缓冲流操作、内存流操作、压缩流操作、网络I/O操作、异步I/O操作、高级I/O操作、I/O性能优化、I/O错误处理、I/O安全性等方面的应用。
掌握I/O操作不仅能够提高代码的健壮性和性能,还能够在复杂应用程序中发挥重要作用。希望本文能帮助读者更好地理解和掌握C#中的流与I/O,在实际开发中充分利用这一强大的编程工具。通过对I/O操作的深入理解和合理应用,可以编写出更加高效、可靠和安全的程序。
