讀寫中文文字檔最常見的亂碼原因是:**編碼不匹配**。`StreamReader`/`StreamWriter` 預設使用 `Encoding.Default`(繁中系統為 Big5,簡中為 GB2312),而檔案實際可能是 UTF-8、UTF-16 LE/BE。本文提供一個 `TxtFileEncoding` 工具類,透過掃描檔案開頭的 **BOM (Byte Order Mark)** 自動判斷編碼,確保讀寫正確無誤。
| 編碼 | BOM 位元組 (Hex) | 說明 |
|---|---|---|
| UTF-8 | EF BB BF | 最常見,建議預設儲存格式 |
| UTF-16 LE (Unicode) | FF FE | Windows 記事本「Unicode」預設 |
| UTF-16 BE (BigEndianUnicode) | FE FF | 網路序/大端序 |
| UTF-32 LE | FF FE 00 00 | 極少用 |
| UTF-32 BE | 00 00 FE FF | 極少用 |
| GB2312 / Big5 / ASCII | 無 BOM | 無法自動偵測,需指定預設編碼 |
using System;
using System.IO;
using System.Text;
namespace Farproc.Text
{
public static class TxtFileEncoding
{
///
/// 取得檔案編碼。無 BOM 時回傳 defaultEncoding(預設 Encoding.Default)。
///
public static Encoding GetEncoding(string fileName, Encoding defaultEncoding = null)
{
defaultEncoding ??= Encoding.Default;
using var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
return GetEncoding(fs, defaultEncoding);
}
///
/// 取得資料流編碼(不關閉 Stream)。
///
public static Encoding GetEncoding(Stream stream, Encoding defaultEncoding = null)
{
defaultEncoding ??= Encoding.Default;
if (stream == null || stream.Length < 2) return defaultEncoding;
long origPos = stream.Position;
stream.Seek(0, SeekOrigin.Begin);
// 讀取前 4 位元組
int b1 = stream.ReadByte();
int b2 = stream.ReadByte();
int b3 = stream.Length >= 3 ? stream.ReadByte() : -1;
int b4 = stream.Length >= 4 ? stream.ReadByte() : -1;
stream.Seek(origPos, SeekOrigin.Begin); // 還原位置
// 依序判斷(優先匹配較長 BOM)
if (b1 == 0xEF && b2 == 0xBB && b3 == 0xBF) return Encoding.UTF8;
if (b1 == 0xFF && b2 == 0xFE && b3 != 0xFF) return Encoding.Unicode; // UTF-16 LE
if (b1 == 0xFE && b2 == 0xFF) return Encoding.BigEndianUnicode; // UTF-16 BE
if (b1 == 0xFF && b2 == 0xFE && b3 == 0x00 && b4 == 0x00) return Encoding.UTF32; // UTF-32 LE
if (b1 == 0x00 && b2 == 0x00 && b3 == 0xFE && b4 == 0xFF) return Encoding.GetEncoding("UTF-32BE"); // UTF-32 BE
return defaultEncoding; // 無 BOM:GB2312/Big5/ASCII 等
}
}
}
string file = @"E:\data.txt";
// 1. 寫入:明確指定編碼(建議 UTF-8)
using (var sw = new StreamWriter(file, false, Encoding.UTF8))
{
sw.WriteLine("這是測試內容");
sw.WriteLine("Second line");
}
// 2. 讀取:自動偵測編碼
Encoding enc = TxtFileEncoding.GetEncoding(file, Encoding.GetEncoding("GB2312")); // 簡中環境預設 GB2312
Console.WriteLine($"偵測編碼: {enc.EncodingName}");
using (var sr = new StreamReader(file, enc))
{
string content = sr.ReadToEnd();
Console.WriteLine(content);
}
GB2312、Big5、ASCII、UTF-8 (無 BOM) 皆無 BOM。可採用以下策略:
Ude.NetStandard (Mozilla Universal Charset Detector 移植版) 或 NTextCat 進行統計分析偵測。| 編碼 | StreamWriter 預設行為 | 建議 |
|---|---|---|
| UTF-8 | **寫入 BOM** (EF BB BF) | 保留,利於自動偵測 |
| UTF-16 LE/BE | 寫入 BOM | 保留 |
| GB2312/Big5 | 無 BOM | 不適用 |
若需**不寫入 UTF-8 BOM**,使用 `new UTF8Encoding(false)`(`encoderShouldEmitUTF8Identifier=false`)。
**BOM 掃描是最輕量、最可靠的編碼自動偵測手段**。建立專案編碼規範(統一 UTF-8+BOM)搭配 `TxtFileEncoding` 工具類,可徹底解決中文亂碼問題。現代 .NET 環境下,`Encoding.Default` 已統一為 UTF-8,跨平台編碼坑點大幅減少,但舊檔案遷移、異構系統整合時,本工具仍具高實用價值。