C# 文件的输入与输出(超详细)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

前言:为何要学习 C# 文件的输入与输出?

在编程的世界里,数据的持久化存储与读取是开发者必须掌握的核心技能之一。无论是保存用户配置、记录系统日志,还是构建复杂的文件处理工具,C# 提供的文件输入输出(I/O)机制都扮演着关键角色。对于编程初学者而言,理解文件操作不仅能够提升代码的实际应用能力,更能为后续学习数据库、网络通信等进阶技术打下坚实基础。本文将从基础概念讲起,通过具体案例和代码示例,带您系统性地掌握 C# 中文件操作的核心方法。


一、文件输入输出的基础概念

1.1 文件操作的核心比喻:数据高速公路

可以将文件操作想象成一辆运输数据的卡车:硬盘是仓库,内存是临时中转站,而文件流(Stream)就是连接两者的高速公路。C# 通过 System.IO 命名空间提供了一系列类库,帮助开发者高效地在这条“数据高速公路”上运输数据。

1.2 关键类库解析

类名功能描述典型应用场景
FileStream与物理文件直接交互的基础类二进制文件操作
StreamReader文本文件的高效读取工具日志文件读取
StreamWriter文本文件的快速写入工具配置文件生成
BinaryReader二进制数据的便捷读取游戏存档读取
BinaryWriter二进制数据的快速写入大数据结构序列化

1.3 文件路径的注意事项

  • 绝对路径 vs 相对路径:C:/data.txt 是绝对路径,./data.txt 是相对路径
  • 路径分隔符:C# 推荐使用 Path.DirectorySeparatorChar 获取系统兼容的分隔符
  • 特殊字符处理:路径中包含空格或特殊符号时,建议使用 @ 符号转义路径

二、文本文件的读写实战

2.1 基础文本读写:Hello World 的升级版

using System.IO;

string path = "output.txt";

// 写入文本
using (StreamWriter writer = new StreamWriter(path))
{
    writer.WriteLine("你好,C# 文件操作!");
}

// 读取文本
using (StreamReader reader = new StreamReader(path))
{
    string content = reader.ReadToEnd();
    Console.WriteLine(content); // 输出结果
}

2.2 高级文本处理技巧

2.2.1 自动换行与编码格式

// 指定编码格式为 UTF-8
using (StreamWriter writer = new StreamWriter(path, true, System.Text.Encoding.UTF8))
{
    writer.WriteLine("支持特殊字符:★❤️");
}

2.2.2 追加模式与覆盖模式

通过 StreamWriter 的第二个参数控制写入模式:

// 追加模式(默认为覆盖模式)
StreamWriter writer = new StreamWriter(path, true);

三、二进制文件的深度操作

3.1 原始数据的直接操作

byte[] data = new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F };

// 写入二进制数据
using (FileStream fs = new FileStream("binary.bin", FileMode.Create))
{
    fs.Write(data, 0, data.Length);
}

// 读取二进制数据
using (FileStream fs = new FileStream("binary.bin", FileMode.Open))
{
    byte[] buffer = new byte[fs.Length];
    fs.Read(buffer, 0, buffer.Length);
    Console.WriteLine(BitConverter.ToString(buffer)); // 输出 "48-65-6C-6C-6F"
}

3.2 复杂结构的序列化

[Serializable]
public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// 二进制序列化
public void SaveUser(User user)
{
    using (FileStream fs = new FileStream("user.dat", FileMode.Create))
    {
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(fs, user);
    }
}

// 反序列化读取
public User LoadUser()
{
    using (FileStream fs = new FileStream("user.dat", FileMode.Open))
    {
        BinaryFormatter formatter = new BinaryFormatter();
        return (User)formatter.Deserialize(fs);
    }
}

四、同步与异步操作的平衡艺术

4.1 同步操作的性能陷阱

// 同步读取大文件可能导致界面卡顿
void SyncReadLargeFile()
{
    using (StreamReader reader = new StreamReader("large_file.txt"))
    {
        while (!reader.EndOfStream)
        {
            string line = reader.ReadLine();
            ProcessLine(line); // 可能耗时操作
        }
    }
}

4.2 异步编程的优雅解决方案

async Task AsyncReadLargeFile()
{
    using (StreamReader reader = new StreamReader("large_file.txt"))
    {
        while (!reader.EndOfStream)
        {
            string line = await reader.ReadLineAsync();
            await ProcessLineAsync(line); // 异步处理
        }
    }
}

4.3 同步与异步的选择原则

场景类型推荐模式关键考量因素
小型配置文件读写同步操作简单快速无需复杂控制
大文件批量处理异步操作避免主线程阻塞
网络+文件混合操作异步模式保持系统响应灵敏度

五、进阶技巧与最佳实践

5.1 异常处理的黄金法则

try
{
    using (var reader = new StreamReader(path))
    {
        // 文件操作代码
    }
}
catch (IOException ex)
{
    // 处理文件被占用的情况
    Console.WriteLine($"文件访问异常:{ex.Message}");
}
catch (UnauthorizedAccessException ex)
{
    // 处理权限不足问题
}
finally
{
    // 资源清理代码(虽然 using 会自动处理,但复杂场景仍需注意)
}

5.2 性能优化的三个维度

  1. 缓冲区大小控制
    // 使用自定义缓冲区提高读写效率
    const int bufferSize = 81920;
    using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize))
    
  2. 批量操作替代循环
    避免逐行读取大文件,改用 Read(buffer, 0, buffer.Length) 一次性读取大块数据
  3. 异步流式处理
    使用 ReadAsyncWriteAsync 结合 ConfigureAwait(false) 优化多线程场景

5.3 文件锁定的解决方案

  • 使用 FileShare 参数控制文件共享模式:
    new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
    
  • 采用 FileModeOpenOrCreate 模式替代 Create 避免独占锁

六、实战案例:文件备份工具开发

6.1 需求分析

  • 自动备份指定目录的文件
  • 保留历史版本(按日期命名)
  • 支持排除特定文件类型

6.2 核心代码实现

public class FileBackup
{
    public static void BackupFolder(string sourcePath, string backupPath)
    {
        // 创建备份目录(按当前日期命名)
        string targetDir = Path.Combine(backupPath, DateTime.Now.ToString("yyyy-MM-dd"));
        Directory.CreateDirectory(targetDir);

        // 遍历源目录
        foreach (var file in Directory.EnumerateFiles(sourcePath))
        {
            if (ShouldExclude(file)) continue;

            string targetFile = Path.Combine(targetDir, Path.GetFileName(file));
            File.Copy(file, targetFile, true);
        }
    }

    private static bool ShouldExclude(string filePath)
    {
        return Path.GetExtension(filePath).Equals(".tmp", StringComparison.OrdinalIgnoreCase);
    }
}

6.3 功能扩展建议

  1. 添加压缩功能(使用 System.IO.Compression 命名空间)
  2. 实现增量备份(对比文件修改时间)
  3. 集成日志记录功能

结论:构建你的文件操作知识体系

通过本文的学习,您应该掌握了 C# 文件操作的核心方法与最佳实践。从基础的文本读写到复杂的二进制处理,从同步操作到异步优化,这些技能将帮助您应对真实开发中的各种场景。建议通过以下步骤深化理解:

  1. 动手实践:尝试实现一个文件加密工具或日志分析器
  2. 查阅文档:深入研究 System.IO 命名空间的完整 API
  3. 性能调优:用性能分析工具测试不同操作模式的效率差异

掌握文件操作不仅是技术能力的提升,更是构建完整系统思维的重要环节。当您能够自如地读写文件、管理资源、处理异常时,便为未来学习数据库操作、分布式系统等进阶技术奠定了扎实基础。继续探索,让数据在您的代码中自由流动!

最新发布