C#StreamWriter 与 File.AppendAllText 写入文本核心区别

C# StreamWriter 与 File.AppendAllText 写入文本核心区别

一、底层本质

1. File.AppendAllText

File.AppendAllText("test.txt", "内容");
  • 静态工具方法,一次性封装全套逻辑
  • 内部流程:
    1. 判断文件是否存在
    2. 创建/打开文件流 FileStream
    3. 实例化 StreamWriter
    4. Write写入
    5. 立刻自动释放所有资源(using 内部封装)
    6. 关闭文件流
  • 源码本质:单次临时创建并销毁 StreamWriter

2. StreamWriter

// 追加模式打开
using var sw = new StreamWriter("test.txt", append: true);
sw.WriteLine("内容");
// 多次写入...
sw.Flush();
  • 流写入类,长生命周期、可复用
  • 需要手动用 using 释放资源,否则文件句柄占用
  • 支持持续多次写入,文件只打开一次

二、核心差异对比表

对比维度 File.AppendAllText StreamWriter(append=true)
文件打开次数 每次调用都重新打开+关闭文件 实例化一次,全程只打开一次
性能(少量写入) 简单代码,差距不大 略优
性能(循环大量写入) 极差,反复创建销毁流,频繁IO 性能极高,一次打开循环写
资源释放 自动释放,无需using 必须using/Dispose,否则锁文件
缓冲区控制 内部自动Flush,无法自定义缓冲 可手动Flush、自定义缓冲区大小
编码设置 重载可指定编码,默认UTF-8带BOM 构造函数自由指定编码
支持逐行/分段 只能一次性传入完整字符串 Write/WriteLine/WriteAsync 灵活分段写
异步版本 File.AppendAllTextAsync WriteAsyncFlushAsync 细粒度异步
文件占用 每次调用瞬间占用后释放 对象存活期间持续持有文件句柄
适用场景 单次少量追加,一行/短句写入 循环批量、大文本、持续分段写入

三、关键细节差异

1. IO 开销(最重要)

场景:循环1万次追加文本

  • File.AppendAllText
    每次打开磁盘文件、创建流、写入、关闭句柄,磁盘频繁读写、系统调用开销巨大,速度慢几十上百倍
  • StreamWriter
    文件仅打开1次,全部写入完成后才关闭,内存缓冲区批量刷盘,IO次数极少。

2. 文件锁与占用

  • File.AppendAllText:方法执行完立刻释放文件,其他程序可随时读写。
  • StreamWriter:只要对象没Dispose,文件会被进程独占,其他程序无法修改/删除该txt。

3. 缓冲区机制

StreamWriter 内置字符缓冲区,默认几千字符:

// 自定义4096字节缓冲区
using var sw = new StreamWriter("test.txt", true, Encoding.UTF8, 4096);
  • 不调用 Flush() 时,数据先存在内存,满了才自动写入磁盘;
  • File.AppendAllText 每次写完强制刷新缓冲区到磁盘。

4. 编码行为

两者都可自定义编码,默认规则一致:

  • 无编码参数:UTF-8(带BOM)
File.AppendAllText("a.txt", "123", Encoding.UTF8);new StreamWriter("a.txt", true, Encoding.UTF8);

5. 异步写法区别

  1. File.AppendAllTextAsync:一次性异步写入整块文本
await File.AppendAllTextAsync("test.txt", "文本");
  1. StreamWriter 细粒度异步,适合流式输出:
using var sw = new StreamWriter("test.txt", true);
await sw.WriteLineAsync("第一行");
await sw.WriteLineAsync("第二行");
await sw.FlushAsync();

四、使用场景推荐

用 File.AppendAllText

  1. 单次、少量追加文本(日志单条记录、简单标记写入)
  2. 代码极简,不想手动管理流、using、释放资源
  3. 写完立刻释放文件,需要其他程序马上读取该文件

用 StreamWriter

  1. 循环批量写入(循环导出数据、持续写日志)
  2. 大文件、多行分段输出,需要多次Write
  3. 需要手动控制缓冲区、异步逐行写入
  4. 追求高性能,减少磁盘打开关闭次数

五、踩坑示例

坑1:循环调用AppendAllText性能灾难

// 不推荐!万次循环极慢
for(int i=0;i<10000;i++)
{File.AppendAllText("log.txt", $"日志{i}\r\n");
}

优化方案改用StreamWriter:

using var sw = new StreamWriter("log.txt", true);
for(int i=0;i<10000;i++)
{sw.WriteLine($"日志{i}");
}
sw.Flush();

坑2:忘记释放StreamWriter导致文件占用

// 错误:无using,文件一直被占用
StreamWriter sw = new StreamWriter("test.txt", true);
sw.WriteLine("xxx");
// 未Dispose,外部无法删除文件// 正确
using var sw = new StreamWriter("test.txt", true);

六、简单总结

  1. 单次少量写入:选 File.AppendAllText,代码简洁省心;
  2. 多次循环/大批量写入:选 StreamWriter,性能碾压前者;
  3. 底层关系:File.AppendAllTextStreamWriter 的一次性封装快捷方法。