直接说结论:想要最快最安全地写入文本文件,首选 File.WriteAllText 或 File.WriteAllLines,但它们会清空原文件;需要追加、逐行处理或精细控制编码时,就必须换 StreamWriter。
覆盖写入:用 File.WriteAllText 和 File.WriteAllLines 就够了
这两个静态方法非常省心:一行代码搞定全部操作,底层自动管理流的打开、写入、关闭,连异常清理都帮你兜着。适合于“我只想把内容一次性存进去,原来文件有没有内容无所谓”的场景。
File.WriteAllText(path, text):写入单个字符串,比如日志摘要、JSON 序列化结果。File.WriteAllLines(path, string[]):写入字符串数组,每项自动换行,导出列表数据时特别好用。- 默认使用 UTF-8 编码(不含 BOM)。如果目标系统要求带 BOM 或 ANSI,务必显式传入
Encoding参数,否则中文很可能乱码。 - 路径不存在时会自动创建目录——注意,这个特性仅限于 .NET 6+;旧版本需要先手动调用
Directory.CreateDirectory。
追加写入:必须用 StreamWriter 并开启 append 模式
File.AppendAllText 看起来简单方便,但每次调用都会新建流、打开文件、写入、再关闭——如果在循环里高频调用,性能会断崖式下降。真正的追加场景,应该复用一个 StreamWriter 实例。
- 构造时用
new StreamWriter(path, true),第二个参数true表示追加模式。 - 务必用
using包裹,否则文件句柄泄漏,后续写入会报IOException: The process cannot access the file。 - 不要在循环里反复
new StreamWriter,哪怕只写几行——开销远远超出你的想象。 - 如果写入的是结构化日志(含时间戳、级别),建议用
file.WriteLine($"{DateTime.Now:O} INFO: {msg}"),避免频繁字符串拼接引发 GC 压力。
逐行过滤或转换后再写入:绕不开 StreamWriter + 手动循环
像“只保存不包含敏感词的行”“给每行加上序号”“把 JSON 数组拆成多行写入”这类需求,File.* 静态方法完全无力招架,只能自己控制写入节奏。
- 别先把全量数据读到内存再过滤——大文件(超过100MB)直接会导致 OOM;改用
StreamReader逐行读 +StreamWriter逐行判断写。 - 注意
StreamWriter.WriteLine()会自动追加\r\n,而Write()不会,两者混用容易造成格式混乱。 - 如果源文件和目标文件路径相同,切忌边读边写——Windows 下会报
UnauthorizedAccessException;正确的做法是:先写到临时文件,再用File.Replace做原子替换。 - 编码必须前后一致:读的时候用什么
Encoding,写的时候也得用同一个,否则中文字符可能被截断或替换成乱码。
容易被忽略的坑:权限、路径、编码、异常处理
你猜怎么着?90% 的“写不进去”问题,其实跟代码逻辑关系不大,而是环境细节没兜住。
- 路径含中文或空格没问题,但
@"C:\test\log.txt"中的反斜杠必须写成双反斜杠或加上 @,写成"C:\test\log.txt"会因\t被解释为制表符而报错。 - 程序没有管理员权限时,往
C:\Windows或C:\Program Files写入必败;普通用户应当写入Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)这类安全目录。 - 捕获
UnauthorizedAccessException和DirectoryNotFoundException,比笼统地 catchException更容易定位问题。 - 调试时发现文件内容为空?先检查一下是否忘了调用
Flush()——虽然Dispose会隐式调用,但手动刷一次可以排除缓冲区滞留的嫌疑。
说到底,真正棘手的不是 API 调用本身,而是你得想清楚:这次写入要不要保留历史、要不要考虑并发安全、目标环境支持什么编码、出错了用户是否需要明确的提示——这些条件决定了你该选哪个 API,而不是反过来。