C#进阶 IO流全面解析
IO(输入/输出)流是C#中处理文件和数据流的核心机制。让我为你详细介绍C#中的IO流系统。
1. 命名空间
using System.IO; // 基础IO操作 using System.Text; // 编码相关
2. 文件基础操作
2.1 System.IO.Path类
System.IO.Path`类是C#中处理文件路径和目录路径的核心工具类。它提供了一系列静态方法,用于跨平台安全地操作路径字符串。
Path类是静态类,不能实例化
Path类中的方法都是平台无关的(Windows/Linux/macOS)
自动处理不同操作系统的路径分隔符
路径分隔符常量
char directorySeparatorChar = Path.DirectorySeparatorChar; // Windows: '\', Linux: '/' char altDirectorySeparatorChar = Path.AltDirectorySeparatorChar; // Windows: '/' char pathSeparator = Path.PathSeparator; // 环境变量分隔符:';' (Windows) 或 ':' (Linux) char volumeSeparatorChar = Path.VolumeSeparatorChar; // 卷分隔符:':' (Windows)
组合路径 - Path.Combine()
// 安全地组合路径(推荐使用) string path1 = @"C:\Users"; string path2 = "Documents"; string path3 = "file.txt"; string fullPath1 = Path.Combine(path1, path2, path3); Console.WriteLine(fullPath1); // C:\Users\Documents\file.txt
// 自动处理斜杠 string path4 = @"C:\Users\"; string path5 = @"Documents\"; string fullPath2 = Path.Combine(path4, path5); Console.WriteLine(fullPath2); // C:\Users\Documents\
获取路径各部分
string fullPath = @"C:\Users\JohnDoe\Documents\report.txt";
// 获取文件名
string fileName = Path.GetFileName(fullPath);
Console.WriteLine($"文件名: {fileName}"); // report.txt
// 获取不带扩展名的文件名
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullPath);
Console.WriteLine($"无扩展名文件名: {fileNameWithoutExtension}"); // report
// 获取扩展名
string extension = Path.GetExtension(fullPath);
Console.WriteLine($"扩展名: {extension}"); // .txt
// 获取目录名
string directoryName = Path.GetDirectoryName(fullPath);
Console.WriteLine($"目录名: {directoryName}"); // C:\Users\JohnDoe\Documents
// 获取根目录
string root = Path.GetPathRoot(fullPath);
Console.WriteLine($"根目录: {root}"); // C:\
// 获取完整路径(规范化路径)
string fullPathWithDot = @"C:\Users\..\Documents\.\report.txt";
string fullNormalizedPath = Path.GetFullPath(fullPathWithDot);
Console.WriteLine($"完整路径: {fullNormalizedPath}"); // C:\Documents\report.txt路径验证和修改
string path = @"C:\test\file.txt";
// 改变扩展名
string newPath = Path.ChangeExtension(path, ".dat");
Console.WriteLine($"新路径: {newPath}"); // C:\test\file.dat
// 检查是否有扩展名
bool hasExtension = Path.HasExtension(path);
Console.WriteLine($"是否有扩展名: {hasExtension}"); // true
// 获取临时文件/目录
string tempFile = Path.GetTempFileName(); // 创建临时文件并返回路径
Console.WriteLine($"临时文件: {tempFile}");
string tempPath = Path.GetTempPath();
Console.WriteLine($"临时目录: {tempPath}"); // C:\Users\用户名\AppData\Local\Temp\
// 获取当前目录
string currentDirectory = Directory.GetCurrentDirectory();
string absolutePath = Path.GetFullPath("relative/path.txt");
// 用户特殊目录
string myDocuments = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);随机文件名生成
// 生成随机文件名
string randomFileName = Path.GetRandomFileName();
Console.WriteLine($"随机文件名: {randomFileName}"); // 如:3b3w8q4e.tmp
// 比较:GetTempFileName vs GetRandomFileName
string tempFile1 = Path.GetTempFileName(); // 实际创建文件
string randomName = Path.GetRandomFileName(); // 只生成名称,不创建文件平台兼容性处理
public class CrossPlatformPathExample
{
public void ProcessPaths()
{
// 错误方式:硬编码路径分隔符
string badPath = "C:\\Users\\Test\\file.txt"; // Windows only
// 正确方式:使用Path类
string goodPath = Path.Combine("C:", "Users", "Test", "file.txt");
// 处理用户输入路径
string userInput = @"C:/Users/Test/file.txt"; // 用户可能使用正斜杠
string normalized = Path.GetFullPath(userInput); // 自动规范化
// 检查非法字符
char[] invalidChars = Path.GetInvalidFileNameChars();
string fileName = "my:file?.txt";
foreach (char invalidChar in invalidChars)
{
fileName = fileName.Replace(invalidChar, '_');
}
Console.WriteLine($"清理后的文件名: {fileName}"); // my_file_.txt
}
}**使用建议:**
- 总是使用 `Path.Combine()` 而不是字符串拼接
- 处理用户输入时,使用 `Path.GetFullPath()` 进行规范化
- 使用 `Path.GetInvalidFileNameChars()` 验证文件名
- 注意 `Path.Combine()` 和 `Path.Join()` 的区别
- 在.NET Core 2.1+中,对于简单连接考虑使用 `Path.Join()`
2.2 System.IO.File类(静态方法)
是C#中处理文件的静态工具类,提供了创建、复制、删除、移动和打开文件的静态方法。
文件存在性检查
string filePath = @"C:\data\test.txt";
// 检查文件是否存在
bool exists = File.Exists(filePath);
if (exists)
{
Console.WriteLine("文件存在");
}检查文件是否只读
FileAttributes attributes = File.GetAttributes(filePath); bool isReadOnly = (attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
文件属性获取
string filePath = @"C:\data\document.txt";
// 获取文件创建时间
DateTime creationTime = File.GetCreationTime(filePath);
Console.WriteLine($"创建时间: {creationTime}");
// 获取最后修改时间
DateTime lastWriteTime = File.GetLastWriteTime(filePath);
Console.WriteLine($"最后修改: {lastWriteTime}");
// 获取最后访问时间
DateTime lastAccessTime = File.GetLastAccessTime(filePath);
Console.WriteLine($"最后访问: {lastAccessTime}");
// 设置文件时间
File.SetCreationTime(filePath, DateTime.Now);
File.SetLastWriteTime(filePath, DateTime.Now);
File.SetLastAccessTime(filePath, DateTime.Now);创建文件
// 创建空文件(如果存在则覆盖)
string filePath = @"C:\temp\newfile.txt";
File.Create(filePath).Close(); // 记得关闭流
// 使用using确保关闭
using (FileStream fs = File.Create(filePath))
{
// 可以在这里写入初始数据
byte[] info = new UTF8Encoding(true).GetBytes("初始内容");
fs.Write(info, 0, info.Length);
}
// 如果只是创建空文件,更简单的方法
File.WriteAllText(filePath, string.Empty);删除文件
string filePath = @"C:\temp\todelete.txt";
// 检查并删除
if (File.Exists(filePath))
{
try
{
File.Delete(filePath);
Console.WriteLine("文件已删除");
}
catch (IOException ex)
{
Console.WriteLine($"删除失败: {ex.Message}");
}
}
// 强制删除(即使只读)
public static void ForceDelete(string filePath)
{
if (File.Exists(filePath))
{
// 移除只读属性
FileAttributes attributes = File.GetAttributes(filePath);
if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
File.SetAttributes(filePath, attributes & ~FileAttributes.ReadOnly);
}
File.Delete(filePath);
}
}文件复制
string sourceFile = @"C:\data\source.txt";
string destFile = @"C:\backup\source_backup.txt";
// 简单复制(目标已存在则抛出异常)
File.Copy(sourceFile, destFile);
// 覆盖复制
bool overwrite = true;
File.Copy(sourceFile, destFile, overwrite);
// 带进度显示的复制
public static void CopyWithProgress(string source, string destination)
{
const int bufferSize = 4096;
using (FileStream sourceStream = new FileStream(source, FileMode.Open, FileAccess.Read))
using (FileStream destStream = new FileStream(destination, FileMode.Create, FileAccess.Write))
{
long totalBytes = sourceStream.Length;
byte[] buffer = new byte[bufferSize];
int bytesRead;
long totalRead = 0;
while ((bytesRead = sourceStream.Read(buffer, 0, bufferSize)) > 0)
{
destStream.Write(buffer, 0, bytesRead);
totalRead += bytesRead;
double progress = (double)totalRead / totalBytes * 100;
Console.WriteLine($"复制进度: {progress:F1}%");
}
}
}移动与重命名
string sourceFile = @"C:\data\oldname.txt";
string destFile = @"C:\data\newname.txt";
// 移动/重命名文件
File.Move(sourceFile, destFile);
// 跨卷移动(自动复制+删除)
string newLocation = @"D:\archive\newname.txt";
File.Move(sourceFile, newLocation);
// 安全的移动操作
public static bool SafeMove(string source, string destination, bool overwrite = true)
{
try
{
if (File.Exists(destination))
{
if (overwrite)
{
File.Delete(destination);
}
else
{
return false; // 目标已存在且不允许覆盖
}
}
File.Move(source, destination);
return true;
}
catch (Exception ex)
{
Console.WriteLine($"移动失败: {ex.Message}");
return false;
}
}读取文本文件
string filePath = @"C:\data\config.txt";
// 读取所有文本
string allText = File.ReadAllText(filePath);
Console.WriteLine($"文件内容:\n{allText}");
// 读取所有行(返回字符串数组)
string[] lines = File.ReadAllLines(filePath);
foreach (string line in lines)
{
Console.WriteLine($"行: {line}");
}
// 使用指定编码
string utf8Text = File.ReadAllText(filePath, Encoding.UTF8);
string[] utf8Lines = File.ReadAllLines(filePath, Encoding.UTF8);
// 读取指定行数
public static List<string> ReadFirstNLines(string filePath, int n)
{
List<string> result = new List<string>();
using (StreamReader reader = new StreamReader(filePath))
{
for (int i = 0; i < n; i++)
{
string line = reader.ReadLine();
if (line == null) break;
result.Add(line);
}
}
return result;
}写入文本文件
string filePath = @"C:\data\log.txt";
// 写入所有文本(覆盖)
string content = "这是新的文件内容";
File.WriteAllText(filePath, content);
// 写入所有行(覆盖)
string[] lines = { "第一行", "第二行", "第三行" };
File.WriteAllLines(filePath, lines);
// 追加文本
string newLine = "这是追加的内容";
File.AppendAllText(filePath, newLine);
// 追加多行
string[] newLines = { "追加的第一行", "追加的第二行" };
File.AppendAllLines(filePath, newLines);
// 追加多行
var newLines = new List<string>{ "追加的第一行", "追加的第二行" };
File.AppendAllLines(filePath, newLines);
// 带编码的写入
File.WriteAllText(filePath, "UTF-8内容", Encoding.UTF8);
File.WriteAllLines(filePath, lines, Encoding.UTF8);2.3 FileInfo类(实例方法)
FileInfo fileInfo = new FileInfo("test.txt");
if (fileInfo.Exists)
{
Console.WriteLine($"文件名: {fileInfo.Name}");
Console.WriteLine($"文件大小: {fileInfo.Length} 字节");
Console.WriteLine($"创建时间: {fileInfo.CreationTime}");
Console.WriteLine($"最后修改时间: {fileInfo.LastWriteTime}");
}3.目录操作
3.1Directory类
// 创建目录
Directory.CreateDirectory(@"C:\MyFolder\SubFolder");
// 检查目录是否存在
if (Directory.Exists(@"C:\MyFolder"))
{
// 获取所有文件
string[] files = Directory.GetFiles(@"C:\MyFolder", "*.txt");
// 获取所有子目录
string[] directories = Directory.GetDirectories(@"C:\MyFolder");
// 删除目录(true表示递归删除)
Directory.Delete(@"C:\MyFolder", true);
}4. 流的基本概念
C#中的流主要分为:
FileStream:文件流
MemoryStream:内存流
NetworkStream:网络流
BufferedStream:缓冲流
4.1 文件读写操作
FileStream(低级文件操作)
// 写入文件
using (FileStream fs = new FileStream("data.bin", FileMode.Create))
{
byte[] data = Encoding.UTF8.GetBytes("Hello, World!");
fs.Write(data, 0, data.Length);
}
// 读取文件
using (FileStream fs = new FileStream("data.bin", FileMode.Open))
{
byte[] buffer = new byte[1024];
int bytesRead = fs.Read(buffer, 0, buffer.Length);
string content = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine(content);
}StreamReader/StreamWriter(文本文件)
// 写入文本文件
using (StreamWriter writer = new StreamWriter("text.txt", false, Encoding.UTF8))
{
writer.WriteLine("第一行");
writer.WriteLine("第二行");
writer.Write("不带换行");
}
// 读取文本文件
using (StreamReader reader = new StreamReader("text.txt", Encoding.UTF8))
{
// 读取全部内容
string allContent = reader.ReadToEnd();
// 逐行读取
string line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
// 读取单个字符
int charCode;
while ((charCode = reader.Read()) != -1)
{
char ch = (char)charCode;
Console.Write(ch);
}
}BinaryReader/BinaryWriter(二进制文件)
// 写入二进制数据
using (BinaryWriter writer = new BinaryWriter(File.Open("data.bin", FileMode.Create)))
{
writer.Write(12345); // int
writer.Write(3.14159); // double
writer.Write(true); // bool
writer.Write("Hello"); // string
}
// 读取二进制数据
using (BinaryReader reader = new BinaryReader(File.Open("data.bin", FileMode.Open)))
{
int intValue = reader.ReadInt32();
double doubleValue = reader.ReadDouble();
bool boolValue = reader.ReadBoolean();
string stringValue = reader.ReadString();
Console.WriteLine($"int: {intValue}, double: {doubleValue}, bool: {boolValue}, string: {stringValue}");
}异步IO操作
// 异步写入
public async Task WriteFileAsync(string path, string content)
{
using (StreamWriter writer = new StreamWriter(path))
{
await writer.WriteAsync(content);
}
}
// 异步读取
public async Task<string> ReadFileAsync(string path)
{
using (StreamReader reader = new StreamReader(path))
{
return await reader.ReadToEndAsync();
}
}
// 使用示例
await WriteFileAsync("async.txt", "异步写入的内容");
string result = await ReadFileAsync("async.txt");内存流(MemoryStream)
// 创建内存流并写入数据
using (MemoryStream ms = new MemoryStream())
{
byte[] data = Encoding.UTF8.GetBytes("内存流数据");
ms.Write(data, 0, data.Length);
// 重置位置到开始
ms.Position = 0;
// 从内存流读取
byte[] buffer = new byte[ms.Length];
ms.Read(buffer, 0, buffer.Length);
string content = Encoding.UTF8.GetString(buffer);
Console.WriteLine(content);
}缓冲流(BufferedStream)
// 使用缓冲流提高性能
using (FileStream fs = new FileStream("largefile.bin", FileMode.Create))
using (BufferedStream bs = new BufferedStream(fs))
{
for (int i = 0; i < 10000; i++)
{
byte[] data = BitConverter.GetBytes(i);
bs.Write(data, 0, data.Length);
}
}文件监控(FileSystemWatcher)
public class FileWatcher
{
public static void WatchDirectory(string path)
{
FileSystemWatcher watcher = new FileSystemWatcher
{
Path = path,
NotifyFilter = NotifyFilters.FileName |
NotifyFilters.DirectoryName |
NotifyFilters.LastWrite,
Filter = "*.txt"
};
// 事件处理
watcher.Created += OnFileCreated;
watcher.Changed += OnFileChanged;
watcher.Deleted += OnFileDeleted;
watcher.Renamed += OnFileRenamed;
watcher.EnableRaisingEvents = true;
}
private static void OnFileCreated(object source, FileSystemEventArgs e)
{
Console.WriteLine($"文件创建: {e.FullPath}");
}
private static void OnFileChanged(object source, FileSystemEventArgs e)
{
Console.WriteLine($"文件修改: {e.FullPath}");
}
private static void OnFileDeleted(object source, FileSystemEventArgs e)
{
Console.WriteLine($"文件删除: {e.FullPath}");
}
private static void OnFileRenamed(object source, RenamedEventArgs e)
{
Console.WriteLine($"文件重命名: {e.OldFullPath} -> {e.FullPath}");
}
}5.NET Core/.NET 5+ 新特性
// 使用新的文件读写API(.NET Core 2.0+)
string content = await File.ReadAllTextAsync("file.txt");
await File.WriteAllTextAsync("file.txt", "新内容");
// 读取所有行
string[] lines = await File.ReadAllLinesAsync("file.txt");
// 读取所有字节
byte[] bytes = await File.ReadAllBytesAsync("file.bin");
await File.WriteAllBytesAsync("file.bin", bytes);6. 实用示例
6.1 复制大文件(带进度显示)
public static void CopyFileWithProgress(string source, string destination)
{
const int bufferSize = 4096; // 4KB缓冲区
using (FileStream sourceStream = new FileStream(source, FileMode.Open))
using (FileStream destStream = new FileStream(destination, FileMode.Create))
{
long totalBytes = sourceStream.Length;
byte[] buffer = new byte[bufferSize];
int bytesRead;
long totalRead = 0;
while ((bytesRead = sourceStream.Read(buffer, 0, bufferSize)) > 0)
{
destStream.Write(buffer, 0, bytesRead);
totalRead += bytesRead;
// 显示进度
double progress = (double)totalRead / totalBytes * 100;
Console.WriteLine($"复制进度: {progress:F2}%");
}
}
}6.2 读取CSV文件
public static List<string[]> ReadCsvFile(string filePath)
{
List<string[]> records = new List<string[]>();
using (StreamReader reader = new StreamReader(filePath))
{
string line;
while ((line = reader.ReadLine()) != null)
{
// 简单的CSV解析(实际项目中建议使用专用库)
string[] fields = line.Split(',');
records.Add(fields);
}
}
return records;
}6.3 最佳实践
1. **始终使用using语句**:确保流资源被正确释放
2. **异常处理**:处理可能发生的IO异常
3. **异步操作**:处理大文件时使用异步方法避免阻塞
4. **路径验证**:验证文件路径的有效性
5. **文件锁定**:注意文件访问权限和锁定问题
try
{
using (FileStream fs = new FileStream("file.txt", FileMode.Open))
{
// 文件操作
}
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"文件未找到: {ex.Message}");
}
catch (IOException ex)
{
Console.WriteLine($"IO错误: {ex.Message}");
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine($"访问被拒绝: {ex.Message}");
}7. 总结
C#的IO流系统提供了丰富而灵活的文件和数据流操作功能。关键点包括:
1. 区分文本流和二进制流的处理方式
2. 使用using语句确保资源释放
3. 异步操作提高应用响应性
4. 根据场景选择合适的流类型
5. 注意异常处理和文件访问权限
掌握这些IO流知识后,你将能够高效地处理各种文件和数据流操作需求。

