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操作的深入理解和合理应用,可以编写出更加高效、可靠和安全的程序。