C#中的 StreamReader/StreamWriter 使用示例詳解
前言
在 C# 開發(fā)中,StreamReader 和 StreamWriter 是處理文本文件的核心類,屬于 System.IO 命名空間。它們基于流(Stream)操作文本數(shù)據(jù),支持讀寫、編碼設(shè)置、異步操作等,適用于日志記錄、配置文件處理、數(shù)據(jù)導(dǎo)出等場景。本文將從基礎(chǔ)到高級(jí)用法,結(jié)合代碼示例,全面解析其核心功能、性能優(yōu)化及常見問題解決方案。
一、什么是 StreamReader 和 StreamWriter?
1. 定義
- StreamReader:用于從流(如文件、網(wǎng)絡(luò)流)中讀取文本數(shù)據(jù),支持逐行讀取、讀取指定長度數(shù)據(jù)或一次性讀取全部內(nèi)容。
- StreamWriter:用于向流中寫入文本數(shù)據(jù),支持追加模式、格式化輸出及自定義編碼。
StreamReader 和 StreamWriter 是 System.IO 命名空間中的兩個(gè)類,它們分別用于讀取和寫入文本數(shù)據(jù)。兩者均繼承自 TextReader 和 TextWriter 抽象類,通常與 FileStream、MemoryStream 等流結(jié)合使用。
2. 特點(diǎn)
- 提供了方便的方法來讀取和寫入文本數(shù)據(jù),支持多種編碼格式。
- 可以處理大文件,通過流的方式逐步讀取或?qū)懭霐?shù)據(jù),節(jié)省內(nèi)存。
- 支持異步操作,提高程序的響應(yīng)性和性能。
3. 用途
- 文本文件讀寫:
- 替代
FileStream直接操作字節(jié)的復(fù)雜性,自動(dòng)處理編碼和換行符。如日志文件、配置文件等。
- 替代
- 網(wǎng)絡(luò)流解析:
- 與
NetworkStream結(jié)合,實(shí)現(xiàn) HTTP 響應(yīng)內(nèi)容的高效解碼。
- 與
- 內(nèi)存流操作:
- 配合
MemoryStream處理內(nèi)存中的文本緩存,如 JSON/XML 序列化。
- 配合
- 日志與數(shù)據(jù)記錄: 支持追加模式寫入,避免頻繁覆蓋文件內(nèi)容。
適用于以下場景:
- 日志記錄:將日志追加到文件。
- 配置文件操作:讀取
.ini、.json等文本配置。 - 數(shù)據(jù)導(dǎo)出:將數(shù)據(jù)寫入 CSV、TXT 文件。
4. 為什么需要 StreamReader/StreamWriter?
在C#中處理文本文件時(shí),直接使用 FileStream 操作字節(jié)數(shù)組不僅繁瑣,還需手動(dòng)處理編碼、換行符等問題。StreamReader 和 StreamWriter 提供了更高級(jí)的文本流操作接口,支持自動(dòng)編碼檢測、換行符處理及便捷的讀寫方法,大幅簡化開發(fā)流程。
二、基礎(chǔ)用法
1. 創(chuàng)建 StreamReader 和 StreamWriter 對(duì)象
在 C# 中,有多種方式可以創(chuàng)建 StreamReader 和 StreamWriter 對(duì)象:
1)從文件路徑創(chuàng)建
使用文件路徑創(chuàng)建 StreamReader 和 StreamWriter 對(duì)象是最常見的方法。
自動(dòng)處理編碼(默認(rèn)UTF-8)
using System.IO;
// 創(chuàng)建 StreamReader
using (StreamReader reader = new StreamReader("example.txt"))
{
// 讀取操作
}
// 創(chuàng)建 StreamWriter
using (StreamWriter writer = new StreamWriter("example.txt"))
{
// 寫入操作
}2)從流創(chuàng)建
也可以從現(xiàn)有的流 Stream對(duì)象創(chuàng)建 StreamReader 和 StreamWriter,例如從 MemoryStream 或網(wǎng)絡(luò)流、 FileStream,適合復(fù)雜場景。
using System.IO;
// 從 MemoryStream 創(chuàng)建
MemoryStream memoryStream = new MemoryStream();
StreamReader reader = new StreamReader(memoryStream);
StreamWriter writer = new StreamWriter(memoryStream);
// 從 FileStream 創(chuàng)建
FileStream fs = new FileStream("data.bin", FileMode.Open);
using (StreamReader sr = new StreamReader(fs)) { /*...*/ }3)編碼與格式設(shè)置
? 指定編碼
默認(rèn)編碼為 UTF-8,但可通過構(gòu)造函數(shù)指定其他編碼(如 UTF-16、ASCII):
// 使用 UTF-16 編碼
using (StreamWriter writer = new StreamWriter("data.txt", false, Encoding.Unicode))
{
writer.WriteLine("Hello, Unicode!");
}? 追加寫入模式
通過指定StreamWriter 的 append參數(shù)為true 設(shè)置為 追加寫入(append: true)。
StreamWriter sw = new StreamWriter("log.txt", true, Encoding.UTF8); // 追加模式? 控制底層流是否關(guān)閉
leaveOpen:控制底層流是否隨讀寫器關(guān)閉(默認(rèn) false)。
MemoryStream ms= new MemoryStream(); StreamReader streamReader = new StreamReader(ms, Encoding.Unicode, leaveOpen: true);
? 自動(dòng)檢測編碼
通過 StreamReader 的 DetectEncodingFromByteOrderMarks 屬性,自動(dòng)識(shí)別 BOM 標(biāo)記:
using (StreamReader reader = new StreamReader("data.txt", Encoding.UTF8, detectEncodingFromByteOrderMarks:true))
{
// 如果文件開頭有 BOM,會(huì)自動(dòng)檢測編碼
string content = reader.ReadToEnd();
}2. 使用 StreamWriter 寫入數(shù)據(jù)
StreamWriter 提供了多種方法來寫入文本數(shù)據(jù),最常用的是 Write 和 WriteLine 方法。
1)寫入數(shù)據(jù)
使用 Write 方法可以寫入數(shù)據(jù)到文件中。支持字符串、數(shù)值等類型。
using (StreamWriter writer = new StreamWriter("example.txt"))
{
// 寫入不同類型的數(shù)據(jù)
writer.Write(1.1f);
writer.Write(42);
writer.Write(new byte[] { 1, 2, 3 });
writer.Write("Hello, World!");
}2)寫入帶換行符的數(shù)據(jù)
使用 WriteLine 方法可以寫入一個(gè)帶換行符的數(shù)據(jù)。支持字符串、數(shù)值等類型。
using (StreamWriter writer = new StreamWriter("example.txt"))
{
// 寫入不同類型的數(shù)據(jù)
writer.WriteLine(1.1f);
writer.WriteLine(42);
writer.WriteLine(new byte[] { 1, 2, 3 });
writer.WriteLine("Hello, World!");
}3. 使用 StreamReader 讀取數(shù)據(jù)
StreamReader 提供了多種方法來讀取文本數(shù)據(jù),最常用的是 Read、ReadLine 和 ReadToEnd 方法。
1)讀取字符
使用 Read 方法可以讀取一個(gè)字符。
using (StreamReader reader = new StreamReader("example.txt"))
{
int character;
while ((character = reader.Read()) != -1)
{
Console.Write((char)character);
}
}2)讀取一行
使用 ReadLine 方法可以讀取一行文本。
using (StreamReader reader = new StreamReader("example.txt"))
{
string line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
}3)讀取所有文本
使用 ReadToEnd 方法可以讀取整個(gè)文件的內(nèi)容。
using (StreamReader reader = new StreamReader("example.txt"))
{
string content = reader.ReadToEnd();
Console.WriteLine(content);
}批量操作:
ReadToEnd()一次性讀取全文,Write()支持字符數(shù)組寫入。
4. StreamReader 和 StreamWriter 的常用屬性和方法
1)StreamReader的常用屬性和方法
BaseStream:獲取StreamReader所使用的基礎(chǔ)流。CurrentEncoding:獲取當(dāng)前使用的字符編碼。EndOfStream:指示是否已到達(dá)流的末尾。Peek:查看下一個(gè)字符而不讀取它。Read:讀取單個(gè)字符或字符數(shù)組。ReadBlock:讀取指定數(shù)量的字符。ReadLine:讀取一行文本。ReadToEnd:讀取流中的所有文本。
2)StreamWriter的常用屬性和方法
BaseStream:獲取StreamWriter所使用的基礎(chǔ)流。AutoFlush:獲取或設(shè)置一個(gè)值,該值指示是否在寫入數(shù)據(jù)后自動(dòng)刷新流。Encoding:獲取當(dāng)前使用的字符編碼。Write:寫入指定的數(shù)據(jù)。WriteLine:寫入指定的數(shù)據(jù),并添加換行符。Flush:將所有緩沖的字符寫入基礎(chǔ)流。Close:關(guān)閉流并釋放所有相關(guān)資源。
3)使用示例
? 獲取當(dāng)前編碼
可以使用 CurrentEncoding 屬性獲取當(dāng)前使用的編碼。
using (StreamReader reader = new StreamReader("example.txt"))
{
Encoding encoding = reader.CurrentEncoding;
Console.WriteLine("Encoding: " + encoding.EncodingName);
}? 獲取基礎(chǔ)流對(duì)象
使用 BaseStream 屬性可以獲取基礎(chǔ)流對(duì)象。
using (StreamReader reader = new StreamReader("example.txt"))
{
Stream stream = reader.BaseStream;
// 操作流
}? 指示是否已到達(dá)流的末尾
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
Console.WriteLine(line);
}5. 示例代碼
下面是一個(gè)完整的示例,演示了如何使用 StreamReader 和 StreamWriter:
class Program
{
static void Main()
{
string filePath = "example.txt";
// 使用 StreamWriter 寫入數(shù)據(jù)
using (StreamWriter writer = new StreamWriter(filePath))
{
writer.WriteLine("Hello, World!");
writer.WriteLine("This is a new line.");
writer.WriteLine("The answer is: 42");
}
// 使用 StreamReader 讀取數(shù)據(jù)
using (StreamReader reader = new StreamReader(filePath))
{
string line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
// 使用 StreamReader 讀取數(shù)據(jù) + EndOfStream 屬性判斷
using (StreamReader reader = new StreamReader(filePath))
{
while (!reader.EndOfStream)
{
Console.WriteLine(reader.ReadLine());
}
}
// 讀取整個(gè)文件內(nèi)容
using (StreamReader reader = new StreamReader(filePath))
{
Console.WriteLine(reader.ReadToEnd());
}
string content = File.ReadAllText(filePath);
Console.WriteLine("File content:");
Console.WriteLine(content);
// 讀取字符
using (StreamReader reader = new StreamReader(filePath))
{
int character;
while ((character = reader.Read()) != -1)
{
Console.Write((char)character);
}
}
}
}通過這個(gè)示例,我們可以看到 StreamReader 和 StreamWriter 在處理文本文件時(shí)是多么方便和高效。它們提供了豐富的功能,滿足了多種文本處理需求。
三、高級(jí)用法
1. 高級(jí)技巧
1)異步操作
使用 ReadAsync、WriteAsync 實(shí)現(xiàn)異步讀寫,避免阻塞主線程,提升I/O性能
public async Task WriteAsync()
{
using (StreamWriter writer = new StreamWriter("data.txt"))
{
await writer.WriteLineAsync("異步寫入");
}
}
public async Task ReadAsync()
{
using (StreamReader reader = new StreamReader("data.txt"))
{
string content = await reader.ReadToEndAsync();
}
}2)緩沖區(qū)優(yōu)化
預(yù)分配容量:若已知文件大小,初始化時(shí)指定 bufferSize 減少擴(kuò)容開銷。 平衡性能與內(nèi)存占用
// 創(chuàng)建帶自定義緩沖區(qū)的流
using (StreamReader reader = new StreamReader("data.txt", Encoding.UTF8, true, 8192))
{
// 緩沖區(qū)大小為 8KB
}3)大文件處理
逐行讀取大文件以避免內(nèi)存溢出:
using (StreamReader reader = new StreamReader("large_file.txt"))
{
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
// 處理每一行
}
}4)顯式刷新緩沖區(qū)
- 顯式刷新緩沖區(qū):高頻寫入時(shí)調(diào)用
Flush()避免內(nèi)存堆積。 - 延遲寫入與批量提交:
sw.AutoFlush = false; // 關(guān)閉自動(dòng)刷新
for (int i = 0; i < 1000; i++) {
sw.Write($"Data {i}");
}
sw.Flush(); // 手動(dòng)批量提交2. 高級(jí)應(yīng)用示例
1)案例1:CSV文件解析
假設(shè)需讀取包含逗號(hào)分隔的CSV文件并提取數(shù)據(jù):
using (StreamReader sr = new StreamReader("data.csv"))
{
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
string[] fields = line.Split(',').Select(f => f.Trim()).ToArray();
// 處理字段數(shù)據(jù)...
}
}優(yōu)勢:自動(dòng)處理編碼與換行符,簡化字符串分割邏輯。
2)案例2:大文件逐行處理與內(nèi)存優(yōu)化
當(dāng)處理 GB 級(jí)日志文件時(shí),需避免一次性加載全部數(shù)據(jù)導(dǎo)致內(nèi)存溢出:
using (var sr = new StreamReader("large.log", Encoding.UTF8, bufferSize: 8192))
{
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
if (line.Contains("ERROR"))
{
// 實(shí)時(shí)處理錯(cuò)誤行
}
}
}優(yōu)化點(diǎn):
- 設(shè)置
bufferSize為 8KB(默認(rèn) 1KB),減少磁盤讀取次數(shù)。 - 逐行釋放內(nèi)存,避免
ReadToEnd()的全量加載風(fēng)險(xiǎn)。
3)案例3:日志記錄系統(tǒng)
public static void Log(string message)
{
using var writer = new StreamWriter("app.log", true);
writer.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}");
}4)案例4:配置文件讀寫
var config = new Dictionary<string, string>();
using var reader = new StreamReader("config.ini");
while ((line = reader.ReadLine()) != null)
{
var parts = line.Split('=');
if (parts.Length == 2)
{
config[parts[0]] = parts[1];
}
}四、常見問題與最佳實(shí)踐
1. 常見問題
1)文件不存在時(shí)的異常
try
{
using (StreamReader reader = new StreamReader("non_existent.txt"))
{
// 處理文件
}
}
catch (FileNotFoundException ex)
{
Console.WriteLine("文件不存在:" + ex.Message);
}2)編碼不匹配導(dǎo)致亂碼
確保讀寫時(shí)編碼一致:
// 寫入時(shí)使用 UTF-8
using (StreamWriter writer = new StreamWriter("data.txt", false, Encoding.UTF8)) { ... }
// 讀取時(shí)指定相同編碼
using (StreamReader reader = new StreamReader("data.txt", Encoding.UTF8)) { ... }3)資源未釋放
始終使用 using 語句確保流正確關(guān)閉:
// 錯(cuò)誤示例:未使用 using
StreamReader reader = new StreamReader("data.txt");
// ... 處理后未關(guān)閉,可能導(dǎo)致文件被鎖定
reader.Close(); // 需手動(dòng)調(diào)用
// 正確做法:使用 using 自動(dòng)釋放資源
using (StreamReader reader = new StreamReader("data.txt")) { ... }
// C# 8+簡化寫法
using var writer = new StreamWriter("output.txt");4)編碼問題
若文件包含BOM(字節(jié)順序標(biāo)記),可通過 detectEncodingFromByteOrderMarks: true 自動(dòng)識(shí)別。
處理含 BOM 頭的多編碼文件時(shí),自動(dòng)適配編碼:
using (FileStream fs = File.OpenRead("mixed_encoding.txt"))
{
using (StreamReader sr = new StreamReader(fs, Encoding.Default, detectEncodingFromByteOrderMarks: true))
{
Console.WriteLine($"檢測到編碼:{sr.CurrentEncoding}");
// 按正確編碼解析內(nèi)容
}
}技巧:通過 CurrentEncoding 屬性獲取實(shí)際使用的編碼。
5)流位置重置
寫入后需調(diào)用 Seek(0, SeekOrigin.Begin) 重置位置,否則后續(xù)讀取會(huì)從末尾開始。
6)避免嵌套流生命周期
避免嵌套流生命周期:確保底層流與讀寫器釋放順序一致(先關(guān)讀寫器,再關(guān)流)。
2. 最佳實(shí)踐總結(jié)
- 資源管理:必須使用using語句
- 編碼明確:盡量指定確定編碼
- 異常處理:全面捕獲IO異常
- 性能考量:大文件使用緩沖讀取
- 模式選擇:追加模式注意參數(shù)設(shè)置
到此這篇關(guān)于C#中的 StreamReader/StreamWriter 使用示例詳解的文章就介紹到這了,更多相關(guān)C# StreamReader/StreamWriter 使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解析C#彩色圖像灰度化算法的實(shí)現(xiàn)代碼詳解
本篇文章是對(duì)C#中彩色圖像灰度化算法的實(shí)現(xiàn)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
C#使用iTextSharp獲取PDF文件書簽信息的操作方法
C# iTextSharp是一個(gè)用于處理PDF文件的源庫,它提供了一系列的功能,包括創(chuàng)建PDF文件,以及提取和操作PDF文件中的內(nèi)容,本文給大家介紹了C#使用iTextSharp獲取PDF文件書簽信息的操作方法,需要的朋友可以參考下2024-04-04
C#數(shù)據(jù)類型實(shí)現(xiàn)背包、隊(duì)列和棧
本文詳細(xì)講解了C#數(shù)據(jù)結(jié)構(gòu)類型,并實(shí)現(xiàn)背包、隊(duì)列和棧的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04
C#實(shí)現(xiàn)二維數(shù)據(jù)數(shù)組導(dǎo)出到Excel的詳細(xì)過程
將數(shù)據(jù)庫查詢出來的數(shù)據(jù)導(dǎo)出并生成?Excel?文件,是項(xiàng)目中經(jīng)常使用的一項(xiàng)功能,本文將介紹通過數(shù)據(jù)集生成二維數(shù)據(jù)數(shù)組并導(dǎo)出到?Excel,文中有詳細(xì)的代碼供大家參考,需要的朋友可以參考下2024-09-09

