C#中的 StreamReader/StreamWriter 使用示例詳解
前言
在 C# 開(kāi)發(fā)中,StreamReader
和 StreamWriter
是處理文本文件的核心類(lèi),屬于 System.IO
命名空間。它們基于流(Stream)操作文本數(shù)據(jù),支持讀寫(xiě)、編碼設(shè)置、異步操作等,適用于日志記錄、配置文件處理、數(shù)據(jù)導(dǎo)出等場(chǎng)景。本文將從基礎(chǔ)到高級(jí)用法,結(jié)合代碼示例,全面解析其核心功能、性能優(yōu)化及常見(jiàn)問(wèn)題解決方案。
一、什么是 StreamReader 和 StreamWriter?
1. 定義
- StreamReader:用于從流(如文件、網(wǎng)絡(luò)流)中讀取文本數(shù)據(jù),支持逐行讀取、讀取指定長(zhǎng)度數(shù)據(jù)或一次性讀取全部?jī)?nèi)容。
- StreamWriter:用于向流中寫(xiě)入文本數(shù)據(jù),支持追加模式、格式化輸出及自定義編碼。
StreamReader
和 StreamWriter
是 System.IO
命名空間中的兩個(gè)類(lèi),它們分別用于讀取和寫(xiě)入文本數(shù)據(jù)。兩者均繼承自 TextReader
和 TextWriter
抽象類(lèi),通常與 FileStream
、MemoryStream
等流結(jié)合使用。
2. 特點(diǎn)
- 提供了方便的方法來(lái)讀取和寫(xiě)入文本數(shù)據(jù),支持多種編碼格式。
- 可以處理大文件,通過(guò)流的方式逐步讀取或?qū)懭霐?shù)據(jù),節(jié)省內(nèi)存。
- 支持異步操作,提高程序的響應(yīng)性和性能。
3. 用途
- 文本文件讀寫(xiě):
- 替代
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ù)記錄: 支持追加模式寫(xiě)入,避免頻繁覆蓋文件內(nèi)容。
適用于以下場(chǎng)景:
- 日志記錄:將日志追加到文件。
- 配置文件操作:讀取
.ini
、.json
等文本配置。 - 數(shù)據(jù)導(dǎo)出:將數(shù)據(jù)寫(xiě)入 CSV、TXT 文件。
4. 為什么需要 StreamReader/StreamWriter?
在C#中處理文本文件時(shí),直接使用 FileStream
操作字節(jié)數(shù)組不僅繁瑣,還需手動(dòng)處理編碼、換行符等問(wèn)題。StreamReader 和 StreamWriter 提供了更高級(jí)的文本流操作接口,支持自動(dòng)編碼檢測(cè)、換行符處理及便捷的讀寫(xiě)方法,大幅簡(jiǎn)化開(kāi)發(fā)流程。
二、基礎(chǔ)用法
1. 創(chuàng)建 StreamReader 和 StreamWriter 對(duì)象
在 C# 中,有多種方式可以創(chuàng)建 StreamReader
和 StreamWriter
對(duì)象:
1)從文件路徑創(chuàng)建
使用文件路徑創(chuàng)建 StreamReader
和 StreamWriter
對(duì)象是最常見(jiàn)的方法。
自動(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")) { // 寫(xiě)入操作 }
2)從流創(chuàng)建
也可以從現(xiàn)有的流 Stream
對(duì)象創(chuàng)建 StreamReader
和 StreamWriter
,例如從 MemoryStream
或網(wǎng)絡(luò)流、 FileStream
,適合復(fù)雜場(chǎng)景。
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
,但可通過(guò)構(gòu)造函數(shù)指定其他編碼(如 UTF-16
、ASCII
):
// 使用 UTF-16 編碼 using (StreamWriter writer = new StreamWriter("data.txt", false, Encoding.Unicode)) { writer.WriteLine("Hello, Unicode!"); }
? 追加寫(xiě)入模式
通過(guò)指定StreamWriter
的 append
參數(shù)為true
設(shè)置為 追加寫(xiě)入(append: true
)。
StreamWriter sw = new StreamWriter("log.txt", true, Encoding.UTF8); // 追加模式
? 控制底層流是否關(guān)閉
leaveOpen
:控制底層流是否隨讀寫(xiě)器關(guān)閉(默認(rèn) false
)。
MemoryStream ms= new MemoryStream(); StreamReader streamReader = new StreamReader(ms, Encoding.Unicode, leaveOpen: true);
? 自動(dòng)檢測(cè)編碼
通過(guò) StreamReader
的 DetectEncodingFromByteOrderMarks
屬性,自動(dòng)識(shí)別 BOM 標(biāo)記:
using (StreamReader reader = new StreamReader("data.txt", Encoding.UTF8, detectEncodingFromByteOrderMarks:true)) { // 如果文件開(kāi)頭有 BOM,會(huì)自動(dòng)檢測(cè)編碼 string content = reader.ReadToEnd(); }
2. 使用 StreamWriter 寫(xiě)入數(shù)據(jù)
StreamWriter
提供了多種方法來(lái)寫(xiě)入文本數(shù)據(jù),最常用的是 Write
和 WriteLine
方法。
1)寫(xiě)入數(shù)據(jù)
使用 Write
方法可以寫(xiě)入數(shù)據(jù)到文件中。支持字符串、數(shù)值等類(lèi)型。
using (StreamWriter writer = new StreamWriter("example.txt")) { // 寫(xiě)入不同類(lèi)型的數(shù)據(jù) writer.Write(1.1f); writer.Write(42); writer.Write(new byte[] { 1, 2, 3 }); writer.Write("Hello, World!"); }
2)寫(xiě)入帶換行符的數(shù)據(jù)
使用 WriteLine
方法可以寫(xiě)入一個(gè)帶換行符的數(shù)據(jù)。支持字符串、數(shù)值等類(lèi)型。
using (StreamWriter writer = new StreamWriter("example.txt")) { // 寫(xiě)入不同類(lèi)型的數(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
提供了多種方法來(lái)讀取文本數(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ù)組寫(xiě)入。
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è)值,該值指示是否在寫(xiě)入數(shù)據(jù)后自動(dòng)刷新流。Encoding
:獲取當(dāng)前使用的字符編碼。Write
:寫(xiě)入指定的數(shù)據(jù)。WriteLine
:寫(xiě)入指定的數(shù)據(jù),并添加換行符。Flush
:將所有緩沖的字符寫(xiě)入基礎(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 寫(xiě)入數(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); } } } }
通過(guò)這個(gè)示例,我們可以看到 StreamReader
和 StreamWriter
在處理文本文件時(shí)是多么方便和高效。它們提供了豐富的功能,滿(mǎn)足了多種文本處理需求。
三、高級(jí)用法
1. 高級(jí)技巧
1)異步操作
使用 ReadAsync
、WriteAsync
實(shí)現(xiàn)異步讀寫(xiě),避免阻塞主線(xiàn)程,提升I/O性能
public async Task WriteAsync() { using (StreamWriter writer = new StreamWriter("data.txt")) { await writer.WriteLineAsync("異步寫(xiě)入"); } } public async Task ReadAsync() { using (StreamReader reader = new StreamReader("data.txt")) { string content = await reader.ReadToEndAsync(); } }
2)緩沖區(qū)優(yōu)化
預(yù)分配容量:若已知文件大小,初始化時(shí)指定 bufferSize
減少擴(kuò)容開(kāi)銷(xiāo)。 平衡性能與內(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ū):高頻寫(xiě)入時(shí)調(diào)用
Flush()
避免內(nèi)存堆積。 - 延遲寫(xiě)入與批量提交:
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)勢(shì):自動(dòng)處理編碼與換行符,簡(jiǎn)化字符串分割邏輯。
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),減少磁盤(pán)讀取次數(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:配置文件讀寫(xiě)
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]; } }
四、常見(jiàn)問(wèn)題與最佳實(shí)踐
1. 常見(jiàn)問(wèn)題
1)文件不存在時(shí)的異常
try { using (StreamReader reader = new StreamReader("non_existent.txt")) { // 處理文件 } } catch (FileNotFoundException ex) { Console.WriteLine("文件不存在:" + ex.Message); }
2)編碼不匹配導(dǎo)致亂碼
確保讀寫(xiě)時(shí)編碼一致:
// 寫(xiě)入時(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
語(yǔ)句確保流正確關(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+簡(jiǎn)化寫(xiě)法 using var writer = new StreamWriter("output.txt");
4)編碼問(wèn)題
若文件包含BOM(字節(jié)順序標(biāo)記),可通過(guò) 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($"檢測(cè)到編碼:{sr.CurrentEncoding}"); // 按正確編碼解析內(nèi)容 } }
技巧:通過(guò) CurrentEncoding
屬性獲取實(shí)際使用的編碼。
5)流位置重置
寫(xiě)入后需調(diào)用 Seek(0, SeekOrigin.Begin)
重置位置,否則后續(xù)讀取會(huì)從末尾開(kāi)始。
6)避免嵌套流生命周期
避免嵌套流生命周期:確保底層流與讀寫(xiě)器釋放順序一致(先關(guān)讀寫(xiě)器,再關(guān)流)。
2. 最佳實(shí)踐總結(jié)
- 資源管理:必須使用using語(yǔ)句
- 編碼明確:盡量指定確定編碼
- 異常處理:全面捕獲IO異常
- 性能考量:大文件使用緩沖讀取
- 模式選擇:追加模式注意參數(shù)設(shè)置
到此這篇關(guān)于C#中的 StreamReader/StreamWriter 使用示例詳解的文章就介紹到這了,更多相關(guān)C# StreamReader/StreamWriter 使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#使用dynamic類(lèi)型訪問(wèn)JObject對(duì)象
這篇文章主要為大家詳細(xì)介紹了C#使用dynamic類(lèi)型訪問(wèn)JObject對(duì)象,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04解析C#彩色圖像灰度化算法的實(shí)現(xiàn)代碼詳解
本篇文章是對(duì)C#中彩色圖像灰度化算法的實(shí)現(xiàn)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C#使用iTextSharp獲取PDF文件書(shū)簽信息的操作方法
C# iTextSharp是一個(gè)用于處理PDF文件的源庫(kù),它提供了一系列的功能,包括創(chuàng)建PDF文件,以及提取和操作PDF文件中的內(nèi)容,本文給大家介紹了C#使用iTextSharp獲取PDF文件書(shū)簽信息的操作方法,需要的朋友可以參考下2024-04-04C#數(shù)據(jù)類(lèi)型實(shí)現(xiàn)背包、隊(duì)列和棧
本文詳細(xì)講解了C#數(shù)據(jù)結(jié)構(gòu)類(lèi)型,并實(shí)現(xiàn)背包、隊(duì)列和棧的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04C#實(shí)現(xiàn)二維數(shù)據(jù)數(shù)組導(dǎo)出到Excel的詳細(xì)過(guò)程
將數(shù)據(jù)庫(kù)查詢(xún)出來(lái)的數(shù)據(jù)導(dǎo)出并生成?Excel?文件,是項(xiàng)目中經(jīng)常使用的一項(xiàng)功能,本文將介紹通過(guò)數(shù)據(jù)集生成二維數(shù)據(jù)數(shù)組并導(dǎo)出到?Excel,文中有詳細(xì)的代碼供大家參考,需要的朋友可以參考下2024-09-09