文章目录
- 一、FileStream和StreamWriter理解
- 1.1、具体关系解析
- 1.2、类比理解
- 1.3、总结
- 1.4、示例代码
- 1.5、 WriteLine()和 Write()的区别
- 1.6、 StreamWriter.Close的作用
- 二、
一、FileStream和StreamWriter理解
在 C# 中,StreamWriter
和 FileStream
是 协作关系,二者共同完成文件的文本写入操作,但职责不同。简单来说:
FileStream
负责底层的 字节级文件操作(如打开、读取、写入字节),而 StreamWriter
基于 FileStream
提供 文本编码转换和便捷的文本写入功能(如直接写入字符串、换行等)。
1.1、具体关系解析
-
依赖关系
StreamWriter
的构造函数需要接收一个Stream
类型的参数(FileStream
是Stream
的子类),即StreamWriter
必须依赖一个底层流(如FileStream
)才能工作。
例:new StreamWriter(aFile)
中,aFile
(FileStream
)是StreamWriter
的“数据源/目标”。 -
功能分工
FileStream
:处理与操作系统交互的底层细节,比如:- 打开文件并获取文件句柄
- 以字节(
byte
)为单位读写数据 - 控制文件的打开模式(
FileMode
)、访问权限(FileAccess
)、共享模式(FileShare
)等。
StreamWriter
:在FileStream
之上封装了文本处理逻辑,比如:- 将字符串自动编码为字节(默认 UTF-8,可指定编码)
- 提供
WriteLine()
等便捷方法(自动处理换行符\r\n
) - 内部维护缓冲区,减少频繁的底层 IO 操作,提升性能。
-
生命周期关联
当StreamWriter
被释放(如using
语句结束)时,会自动刷新缓冲区并关闭其依赖的FileStream
(除非构造时指定leaveOpen: true
)。
例:若希望释放StreamWriter
后仍保留FileStream
用于其他操作,可这样写:using (FileStream aFile = new FileStream("Log.txt", FileMode.OpenOrCreate)) {// leaveOpen: true 表示释放 StreamWriter 时不关闭 FileStreamusing (StreamWriter sw = new StreamWriter(aFile, leaveOpen: true)){sw.WriteLine("Hello");}// 此时 aFile 仍可继续使用(如读取字节) }
1.2、类比理解
可以把二者的关系类比为“水管”和“水龙头”:
FileStream
是 水管:负责连接水源(文件),提供原始的水流(字节)传输通道。StreamWriter
是 带过滤功能的水龙头:安装在水管上,将原始水流(字节)转换为符合需求的“饮用水”(字符串),并提供便捷的开关(写入方法)。
1.3、总结
FileStream
是底层字节流,处理文件的物理操作;StreamWriter
是高层文本包装器,依赖FileStream
实现字符串到字节的转换和便捷写入。- 二者结合使用,既利用了
FileStream
的底层控制能力,又通过StreamWriter
简化了文本处理。
1.4、示例代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;namespace StreamWrite
{class Program{static void Main(string[] args){try{FileStream aFile = new FileStream("Log.txt", FileMode.OpenOrCreate);StreamWriter sw = new StreamWriter(aFile);bool truth = true;// Write data to file.sw.WriteLine("Hello to you.");sw.WriteLine("It is now {0} and things are looking good.",DateTime.Now.ToLongDateString());sw.Write("More than that,");sw.Write(" it's {0} that C# is fun.", truth);sw.Close();}catch(IOException e){Console.WriteLine("An IO exception has been thrown!");Console.WriteLine(e.ToString());Console.ReadLine();return;}}}
}
1.5、 WriteLine()和 Write()的区别
sw.WriteLine("Hello to you.");sw.WriteLine("It is now {0} and things are looking good.",DateTime.Now.ToLongDateString());sw.Write("More than that,");
在 C# 的 StreamWriter
类中,WriteLine()
和 Write()
是两个常用的写入方法,它们的核心区别在于 是否自动添加换行符。以下是详细对比:
一、核心区别
方法 | 是否添加换行符 | 写入后光标位置 | 示例代码效果 |
---|---|---|---|
WriteLine() | ✅ 自动添加 | 移至下一行开头 | sw.WriteLine("A"); sw.WriteLine("B"); → 文件内容:A B |
Write() | ❌ 不添加 | 留在当前行末尾 | sw.Write("A"); sw.Write("B"); → 文件内容:AB |
二、示例对比
假设你有以下代码:
using (StreamWriter sw = new StreamWriter("test.txt"))
{sw.Write("第一行");sw.WriteLine("第二行");sw.Write("第三行");sw.WriteLine("第四行");
}
生成的 test.txt
文件内容为:
第一行第二行
第三行第四行
三、使用场景建议
-
逐行写入文本(如日志、CSV):
使用WriteLine()
,例如:sw.WriteLine("Name, Age"); sw.WriteLine("Alice, 30"); sw.WriteLine("Bob, 25");
-
格式化输出(如拼接字符串):
使用Write()
配合WriteLine()
,例如:sw.Write("Total: "); sw.WriteLine(100.50); // 输出:Total: 100.50
-
手动控制换行:
使用Write()
并显式添加换行符(\n
或Environment.NewLine
),例如:sw.Write("第一部分内容\n"); // 等效于 sw.WriteLine("第一部分内容"); sw.Write("第二部分内容" + Environment.NewLine); // 跨平台换行符
四、性能差异
两者的性能差异极小,可忽略不计。但在大量写入时:
WriteLine()
可能略微慢于Write()
(多一次换行符写入)。- 建议结合
BufferedStream
提升整体性能:using (FileStream fs = new FileStream("test.txt", FileMode.Create)) using (BufferedStream bs = new BufferedStream(fs, 8192)) // 8KB 缓冲区 using (StreamWriter sw = new StreamWriter(bs)) {// 批量写入 }
五、注意事项
-
换行符的平台差异:
- Windows 默认使用
\r\n
(回车+换行)。 - Linux/macOS 使用
\n
(换行)。 WriteLine()
会根据当前操作系统自动选择换行符(通过Environment.NewLine
)。
- Windows 默认使用
-
与
Console.WriteLine()
的类比:StreamWriter
的WriteLine()
和Console.WriteLine()
逻辑一致,只是输出目标不同(文件 vs 控制台)。
六、总结
- 用
WriteLine()
:当需要每行独立时(如日志、配置文件)。 - 用
Write()
:当需要自定义格式或拼接内容时(如 JSON、HTML)。 - 混用:当需要灵活控制换行位置时(如表格输出)。
合理选择方法可以让代码更简洁、输出更易读。
1.6、 StreamWriter.Close的作用
在 C# 中,sw.Close()
的作用是 释放 StreamWriter
占用的系统资源(如文件句柄、网络连接等)。如果不调用 Close()
,会导致以下问题:
一、不调用 Close()
的后果
-
资源泄漏
- 文件句柄不会立即释放,其他程序无法访问该文件(如无法删除或修改)。
- 若频繁创建
StreamWriter
而不关闭,可能耗尽系统资源(如文件句柄上限)。
-
数据丢失风险
StreamWriter
有 缓冲区(默认 4KB),数据会先写入缓冲区,而非直接写入文件。- 若不调用
Close()
或Flush()
,缓冲区中的数据可能 不会被写入文件。
-
异常处理不完善
- 若写入过程中发生异常,
Close()
可能不会被执行,导致资源无法释放。
- 若写入过程中发生异常,
二、Close()
的底层逻辑
Close()
方法实际上做了两件事:
Flush()
:将缓冲区中的数据强制写入文件。Dispose()
:释放非托管资源(如文件句柄),并标记对象已销毁。
三、推荐替代方案:using
语句
C# 提供了 using
语句,它会自动调用 Dispose()
方法(等价于 Close()
),即使发生异常也能确保资源释放。
示例对比:
// 不使用 using(需手动 Close())
StreamWriter sw = new StreamWriter("test.txt");
sw.WriteLine("Hello");
sw.Close(); // 必须显式调用,否则资源泄漏// 使用 using(自动释放资源)
using (StreamWriter sw = new StreamWriter("test.txt"))
{sw.WriteLine("Hello");// 无需调用 Close(),using 块结束时自动释放
}
四、为什么示例代码中不调用 Close()
仍“正常”?
-
程序退出时系统回收资源
若程序立即退出,操作系统会强制释放文件句柄,但这是不可控的行为,不推荐依赖。 -
缓冲区自动刷新
- 缓冲区满时(默认 4KB)会自动刷新。
- 若写入的数据量很小(如示例中的几行文本),可能未填满缓冲区,导致数据丢失。
五、最佳实践
-
永远使用
using
语句
避免手动调用Close()
,让using
自动管理资源。 -
显式调用
Flush()
(可选)
若需要立即写入数据(如实时日志),可调用Flush()
:using (StreamWriter sw = new StreamWriter("log.txt")) {sw.WriteLine("重要日志");sw.Flush(); // 立即写入文件,不等待缓冲区满 }
-
处理大文件时增大缓冲区
通过构造函数调整缓冲区大小(如 64KB),减少 IO 次数:using (FileStream fs = new FileStream("large.txt", FileMode.Create)) using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8, 65536)) {// 写入大量数据 }
六、总结
Close()
的作用:释放资源并确保数据写入文件。- 不调用的风险:资源泄漏、数据丢失。
- 推荐方案:使用
using
语句替代手动Close()
,提升代码安全性和简洁性。