由於從 .NET Core 1.0 開始,就沒有自動載入 BIG5
編碼的 Encoding
資料,所以你沒辦法直接透過 Encoding.GetEncoding("Big5")
取得 Encoding
物件。在 .NET Core 3.1 之前,還需要須額外安裝 System.Text.Encoding.CodePages
套件才行。不過,從 .NET 5 開始,這個套件成為了 .NET SDK 的一部分,不再需要額外安裝。但即便如此,你還是要特別執行一段註冊以使用此編碼。接下來,我們將深入探討如何註冊這些編碼並正確使用它們在不同的場景中。

快速摘要
如果需要取得 Encoding.GetEncoding("Big5")
編碼,請在程式啟動時加入以下程式碼:
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Encoding 概述
System.Text.Encoding
是 .NET 中用於文字編碼轉換的核心類別。它的作用是在字元和位元組之間進行轉換:所謂編碼 (Encoding) 就是將一組 Unicode 字元轉換成對應的位元組序列,反之則稱為解碼 (Decoding)。
由於 .NET 字串 (string
) 內部使用 UTF-16
編碼 (Unicode 編碼) 來表示文字,而每個 char
通常對應 Unicode 碼元(Code Unit
),Encoding
類別的角色就是在程式的 Unicode 字串與各種外部編碼形式之間架起橋樑。例如,讀取檔案或網路資料時,可能需要將位元組依照正確的編碼解碼成 .NET 字串;反之,寫出文字或傳輸資料時,也需要依照指定編碼將字串轉為位元組。
需要注意的是,Encoding
類別設計用來處理文字資料,而非任意的二進位內容。如果要將任意二進位資料轉成字串儲存 (例如將圖片或加密資料以字串表示),不應直接使用文字編碼 (否則可能造成資料遺失或損毀),而應採用例如 Base64
等專門的編碼方式。
碼點 (code point) 與 碼元 (code unit)
碼點 (code point) 與 碼元 (code unit) 是字元編碼領域中的兩個重要概念,它們在字元表示和儲存中扮演著不同的角色。
-
碼點 (Code point)
是指在編碼字元集中,每個字元所對應的唯一數值。例如,在 Unicode 中,字元 "A" 的碼點是 U+0041
。
-
碼元 (code unit)
則是指在特定編碼形式中,用於表示碼點的最小資料單位。
不同的編碼方式,其碼元的大小可能不同。例如,在 UTF-8 編碼中,每個碼元為 8 位元;而在 UTF-16 編碼中,每個碼元為 16 位元。
簡單來說,碼點 (code point) 是對應於字元的抽象數值,而 碼元 (code unit) 則是具體編碼方式中用來表示這些碼點的實際資料單位。
常見的編碼類型
常見的文字編碼方式有許多種,各自適用於不同情境。以下列出幾種常用編碼及其特性:
-
UTF-8
一種 Unicode (萬國碼) 的實作,使用可變長度的位元組序列來編碼字元,英文字母等基本字元只需 1 個位元組,歐洲及中東字元通常 2 個位元組,東亞文字可能需要 3~4 個位元組。
UTF-8 能表示所有 Unicode 字元,具有良好的儲存效率和廣泛的相容性,因此成為網際網路及跨平台傳輸的事實標準。以 UTF-8 編碼的文字沒有語系限制,非常適合多語言環境。
-
UTF-16
Unicode 的 16 位元編碼形式,每個字元通常使用 2 個位元組表示。
依照位元序不同可分為 UTF-16 LE (Little Endian,小端序) 與 UTF-16 BE (Big Endian,大端序)。
在 Windows 等主流平台上常用 UTF-16 LE
(.NET 的 Encoding.Unicode
屬性即代表 UTF-16 LE 編碼。
UTF-16 同樣可表示全部 Unicode 字元,但因固定每字元至少 2 位元組,對以拉丁字母為主的內容來說檔案體積會比 UTF-8 大一些。
UTF-16 BE 則主要在某些大型主機或特定系統使用,使用時須注意大小端序需與資料對應。
-
UTF-32
Unicode 的 32 位元編碼形式,每個字元固定使用 4 個位元組表示 (可直接對應 Unicode 碼點)。
Encoding.UTF32
提供了 UTF-32 LE 編碼(以及透過建構函式可建立 UTF-32 BE 編碼。
UTF-32 因為每個字元占用空間大,一般較少用於儲存或傳輸,但在需要固定長度表示字元的場景下可能會用到。
-
ASCII
一種早期的 7 位元編碼,只定義了 128 個字元 (包括英文字母、數字、標點符號和控制字元)。
在 .NET 中可透過 Encoding.ASCII
使用此編碼。ASCII 編碼不支援中文、日文等任何超出 U+007F
的字元,所以僅適用於純英文環境。
如內容包含非 ASCII 字元,使用 ASCII 編碼將無法正確表示,未支援的字元通常會被遺失或替換成問號。
-
ANSI / 碼頁 (Code Page)
ANSI
並非一種單一定義的編碼,而是對各地區預設碼頁的統稱。
例如 美國 / 西歐 Windows 的預設 ANSI 編碼是 Windows-1252
(類似 ISO-8859-1
西歐編碼),繁體中文 Windows 的 ANSI 編碼則是 Big5
,日文 Windows 則是 Shift-JIS
等。
這類編碼大多採用單/雙位元組混合的形式表示字元,也就是所謂「延伸 ASCII」或傳統碼頁 (Code Page) 編碼。
下面分別介紹幾個代表:
-
Big5
早期繁體中文地區使用的主要編碼,屬於雙位元組編碼。
Big5
由台灣五大電腦公司在 1980 年代制定,名稱即取自「五大碼」 (Big5 - Wikipedia)。
Big5
編碼可涵蓋常用的繁體中文字以及基本的拉丁字母和符號,在台灣、香港、澳門曾廣泛用於文件、郵件和 Windows 系統預設語系。現今 UTF-8
普及後,Big5
主要在舊系統相容或歷史資料中會見到。
-
Shift-JIS
日本使用的傳統編碼 (又稱 SJIS
),也是一種可變長度的雙位元組編碼。
Shift-JIS
由日本的 ASCII 公司和微軟共同制定,結合了 JIS
標準的單位元假名與雙位元漢字 (Shift JIS - Wikipedia)。
在 Unicode 普及前,Shift-JIS
一直是日文 Windows 和網站的主要編碼 (Shift JIS - Wikipedia)。現今在日文環境中 UTF-8 逐漸成為主流,不過 Shift-JIS 仍可在某些舊網站、電子郵件或檔案中遇到。
在 .NET 中,Shift-JIS
對應的碼頁編號是 932
(Windows-31J
是其超集)。若需處理日文舊系統資料,可能需要用到此編碼。
除了上述之外,還有例如 ISO-8859 系列 (拉丁語系各國的單位元編碼) 以及其他國家 / 地區的本地編碼 (如簡體中文的 GB2312
/GBK
/GB18030
等)。但整體趨勢是 Unicode已成主流,建議在可能情況下都優先使用 Unicode
編碼 (UTF-8/UTF-16) 處理文字,以避免不同編碼轉換造成的困擾。
如何取得與使用編碼
在 .NET 中,可以透過多種方式取得對應的 Encoding
物件:
-
靜態屬性
Encoding
類別直接提供了幾個常用編碼的靜態屬性,例如 Encoding.UTF8
、Encoding.Unicode
(UTF-16 LE)、Encoding.BigEndianUnicode
(UTF-16 BE)、Encoding.ASCII
等,可直接使用這些屬性取得對應的編碼實例。
這些屬性涵蓋了 ASCII 和主要的 Unicode 編碼,通常是最常用的選擇。
-
類別建構函式
對於 ASCII、UTF7、UTF8、UTF16、UTF32 等,也可以直接呼叫相應的編碼類別的建構子來建立實例,例如 new UTF8Encoding()
、new UnicodeEncoding()
等。
靜態屬性其實已提供單例實例(Singleton),一般不需要重複建立,但透過建構函式可以設定一些參數 (例如是否提供 BOM
或錯誤回退行為等)。
-
GetEncoding 方法
Encoding.GetEncoding(name或codePage)
是一種更通用的取得編碼實例的方法。它允許你以編碼名稱 (如 "UTF-8"
, "Big5"
, "Shift_JIS"
, "windows-1252"
等) 或碼頁編號 (code page,如 950、932 等) 來取得對應的編碼。例如:
Encoding utf8 = Encoding.GetEncoding("UTF-8");
Encoding big5 = Encoding.GetEncoding("Big5");
Encoding big5 = Encoding.GetEncoding(950); // 950 是 Big5 碼頁
Encoding sjis = Encoding.GetEncoding("Shift_JIS");
然而,需要注意並非所有編碼在 .NET Core/.NET 5+ 中都直接可用。在 .NET Framework 上,GetEncoding
幾乎可以取得任何已安裝於 Windows 的碼頁編碼。但在 .NET Core 平台,出於精簡預設僅內建支援少數幾種編碼 (主要是 Unicode 系列和 latin-1 等)。
例如:如果直接在 .NET Core 3.1 或 .NET 5 的程式中呼叫 Encoding.GetEncoding("Big5")
,會拋出例外,訊息提示「不支援的編碼」或建議使用 Encoding.RegisterProvider
方法。這是因為 .NET Core 預設不包含 Big5、Shift-JIS 等傳統碼頁。
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
-
Encoding.GetEncodings
如果想查看目前環境支援哪些編碼,可以呼叫 Encoding.GetEncodings()
列出所有可用的編碼資訊。
但如上所述,在未額外設定下 .NET Core 列出的清單會相對有限。
支援額外編碼 (安裝 CodePagesEncodingProvider)
為了在 .NET Core / .NET 5+ 中使用 Big5、Shift-JIS、Windows-125x 等額外的編碼,需要引入 CodePagesEncodingProvider。具體作法是:
-
安裝套件
在專案中安裝 NuGet 套件 System.Text.Encoding.CodePages
。此套件包含了對各種碼頁的支援資料。
How to read Windows-1252 encoded files with .NETCore and .NET5+ | Nicola Iarocci
-
註冊提供者
在程式啟動時 (例如 Main
方法或應用程式初始化時) 呼叫,只需要呼叫一次:
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
這行程式會註冊一個編碼提供者(CodePagesEncodingProvider),讓 .NET Core 能夠使用 .NET Framework 提供的那些傳統碼頁編碼。註冊後,先前不支援的編碼就會被加入可用清單中。
-
取得編碼
提供器註冊後,即可正常使用 Encoding.GetEncoding()
來取得所需的編碼物件,例如 Encoding.GetEncoding("Big5")
或 Encoding.GetEncoding("Shift_JIS")
,不再拋出不支援例外。之後便可像使用其他 Encoding 一樣讀寫轉換文字。
下面是完整的範例流程:假設需要讀取 Big5 編碼的檔案,可這樣撰寫程式碼:
using System.Text;
// 安裝了 System.Text.Encoding.CodePages 套件後:
using System.Text.Encoding.CodePages;
// ... 程式啟動時:
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// 讀取 Big5 編碼的文字檔
Encoding big5 = Encoding.GetEncoding("Big5");
string content = File.ReadAllText("data_big5.txt", big5);
經過上述步驟,Encoding
類別就能支援大部分傳統編碼 (涵蓋各國碼頁),這對需要處理舊檔案或與非 Unicode 系統相容性時非常有用 (How to read Windows-1252 encoded files with .NETCore and .NET5+ | Nicola Iarocci)。請注意,註冊動作只需要執行一次 (通常在應用程式啟動時),一旦註冊,後續所有 GetEncoding
呼叫都能取得額外的編碼,不需要每次重複註冊提供者。
跨平台相容性
由於 .NET Core/.NET 5+ 是跨平台的運行時環境,在不同作業系統上的編碼支援略有差異,需要特別注意以下幾點:
-
預設支援的編碼
正如前面所述,.NET Core 為減少體積僅內建支援少數編碼,除了 Unicode 家族 (UTF-8/16/32) 和 ASCII 以及 ISO-8859-1 (碼頁 28591) 外,其他如 Big5、Shift-JIS、Windows-1252 等都不內建。相對地,傳統 .NET Framework (僅限 Windows) 則憑藉作業系統提供了全面的碼頁支援。
因此,如果一段程式碼在 .NET Framework 上可以直接使用 Encoding.GetEncoding(1252)
取得 Windows-1252
編碼,在 .NET Core 上執行則需要先安裝套件並註冊提供者,否則會報錯「沒有可用的編碼」。
-
Encoding.Default
在 .NET Framework 上,Encoding.Default
屬性會返回當前系統的 ANSI
編碼,例如美國英文系統通常是 Windows-1252
,繁體中文系統則會是 Big5
,依據作業系統的地區設定而定。
但在 .NET Core/5+ 中,為了跨平台一致性,Encoding.Default
被固定定義為 UTF-8 編碼。也就是說,在 Linux、macOS 或 Windows 上,.NET Core 的 Default
都是 UTF-8
,而不再是各自系統的本地碼頁,這點差異必須特別注意。
因此,不要依賴 Encoding.Default
來猜測本地編碼,因為在新平台下它並不代表使用者環境的語系;若需要特定編碼,應明確指定。例如,以下程式在 .NET Framework 和 .NET 6 上會有不同結果:
Console.WriteLine(Encoding.Default.BodyName);
在 .NET Framework (假設系統地區為台灣) 可能輸出 Big5
,但在 .NET 6 不論系統語言都會輸出 utf-8
。這項差異提醒我們,在跨平台程式中最好自行指定編碼,而非使用預設值。
-
不同 OS 的編碼行為
由於各作業系統底層對編碼支援不同,某些情境下也要注意。例如在 Linux 上跑 .NET,如果沒有透過 CodePagesEncodingProvider
,引入的碼頁資料其實來自 .NET 的內建對照表,而非作業系統。
所以一旦註冊了 CodePagesEncodingProvider,編碼行為在各平台應該是一致的 —— 因為都使用相同的對應表。
不過仍建議盡量使用 Unicode 等通用編碼來進行跨平台文字交換,因為這樣最簡單也最不易出錯。如果不得不使用如 Big5
等區域性編碼,也要確保所有執行環境都有安裝相應提供器,並在測試時於不同 OS 上驗證文字顯示正確無誤。
總之,在開發跨平台 .NET 應用時,應養成明確指定編碼的習慣,並利用 Unicode 編碼來統一處理資料,只有在需要相容時才使用特定碼頁且正確載入支援。這能最大程度確保 Windows、Linux、Mac 上輸入輸出文字的一致性。
實作範例
下面透過幾個常見的場景,說明如何正確使用 Encoding
類別來讀寫資料,避免發生亂碼或編碼錯誤。
-
讀取與寫入文字檔案
日常開發中經常需要讀寫文字檔,正確處理編碼能避免產生亂碼的問題。例如,我們有一個文字檔,其實內容是以 Big5 編碼儲存的繁體中文,但若程式錯誤地以 UTF-8 去解讀它,就可能出現「鬼畫符」般的亂碼。

如上圖所示,當以錯誤的編碼解讀文字時,會出現無意義的亂碼。展示了原本以 UTF-8 編碼的日文維基百科頁面,如果錯用 Windows-1252
編碼去解讀時的情況 —— 文本變成一連串毫無意義的符號。類似地,如果讀取檔案時編碼搞錯,也會得到亂七八糟的內容甚至問號。同理,寫出檔案時若編碼不對,其他應用開啟時也可能是亂碼。
避免亂碼的關鍵在於讀寫雙方約定使用相同的編碼。例如,若知道檔案是 Big5 編碼,就要用 Big5 去讀取。在 .NET 中,可以使用 File.ReadAllText
/File.WriteAllText
等方法並指定編碼參數,或使用 StreamReader
/StreamWriter
來完成。例如:
// 讀取 Big5 編碼的檔案
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // 確保支援Big5
string txt = File.ReadAllText("article.txt", Encoding.GetEncoding("Big5"));
// 寫出 UTF-8 編碼的檔案
string output = "你好,Hello!";
File.WriteAllText("output.txt", output, Encoding.UTF8);
上例中,我們在讀取前註冊了編碼提供器以支援 Big5,然後明確指定使用 Big5 去解碼檔案內容。寫出檔案時則指定以 UTF-8 編碼輸出字串。如此一來,就不會發生亂碼問題。
使用 StreamReader
/StreamWriter
時情況類似:
// 使用 StreamReader 讀取 (假設檔案為 Shift_JIS 編碼)
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
using var reader = new StreamReader("data_sjis.txt", Encoding.GetEncoding("Shift_JIS"));
string line;
while((line = reader.ReadLine()) != null) {
// 處理每行文字
}
// 使用 StreamWriter 寫入 (例如寫入 UTF-16 編碼檔案)
using var writer = new StreamWriter("log_unicode.txt", false, Encoding.Unicode);
writer.WriteLine("記錄一條日誌...");
特別地,StreamReader
在未指定編碼時,.NET Core 中預設會以 UTF-8 嘗試解碼 (並自動偵測 UTF-8/UTF-16 的 BOM)。如果檔案本身不是 UTF-8,這時就可能解讀錯誤。因此對於沒有 BOM 且非 UTF-8 編碼的檔案,更要明確指定正確的編碼。總之,讀寫檔案最好始終指定編碼,以免依賴預設值而發生預料外的結果。
-
網路請求與回應 (API)
在進行 HTTP 通訊或呼叫 API 時,也經常涉及文字的編碼。例如發送一個 JSON 請求,預設應使用 UTF-8 編碼內容;又或者請求一個網頁,需根據對方提供的編碼來解碼回應。
請求方面:使用 .NET 的 HttpClient
時,可以透過 StringContent
指定內容的編碼和媒體類型。例如:
var json = "{\"name\":\"測試\"}";
var content = new StringContent(json, Encoding.UTF8, "application/json");
await httpClient.PostAsync(apiUrl, content);
上述程式將確保發出的 HTTP 請求主體使用 UTF-8 編碼,並告知伺服器內容類型為 JSON (包含 charset=UTF-8)。大部分網路 API 都預設採用 UTF-8 傳輸文字資料,如果需要其他編碼 (較少見),也應在此明確指定,例如:
var json = "{\"name\":\"測試\"}";
var content = new StringContent(json, Encoding.GetEncoding("Big5"), "application/json");
await httpClient.PostAsync(apiUrl, content);
回應方面:接收資料時要依 Content-Type 的字元編碼來解碼。比如使用 HttpClient
時,若知道回應是 UTF-8,可直接用 response.Content.ReadAsStringAsync()
取得字串 (內部會據 content-type 的 charset 解碼)。但如果回應是其他編碼 (假設一個老舊服務回傳 Big5 編碼的 HTML),可以這樣處理:
byte[] bytes = await httpClient.GetByteArrayAsync(url);
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
string html = Encoding.GetEncoding("Big5").GetString(bytes);
先取得位元組陣列(byte[]
),再用正確的編碼解碼成字串。
針對使用 ASP.NET 建構的 Web 應用,框架本身也提供編碼設定的屬性。例如可以將 HttpResponse.ContentEncoding
設定為與請求相同的編碼,確保送回給用戶端的頁面使用的是它可以正確顯示的編碼。
通常情況下,目前瀏覽器與服務端通訊都以 UTF-8 為主,但如果你的網站需要支持特定語系 (如繁中 zh-TW 頁面),務必在 Response 設定對應的 Encoding,並在 HTML <meta charset>
或 HTTP Header 中宣告編碼,讓瀏覽器正確渲染。
-
資料庫存取
資料庫的文字處理雖然大多對開發者透明,但仍有一些最佳做法來避免編碼問題。現代關聯式資料庫 (如 SQL Server、MySQL、PostgreSQL 等) 通常都支援 Unicode 欄位類型 (例如 NVARCHAR
/NCHAR
等類型),建議優先使用這些欄位儲存文字,以便資料庫和 .NET 應用之間以 Unicode 字串直接交換資料,而不需考慮中間編碼轉換。
如果你的資料庫欄位使用非 Unicode 編碼 (例如某歷史系統的資料庫使用 Big5 編碼儲存文字),那在存取時就要特別小心一致性。一般透過 ADO.NET 提供者 (例如 System.Data.SqlClient 或 MySQL Connector 等) 查詢資料時,驅動程式會參考連線字元集或資料庫的編碼設定,自動將資料轉為 .NET 的 Unicode 字串。然而前提是連線字串或資料庫屬性要正確配置。例如 MySQL 連線字串通常需要指定 Charset=utf8
或其他編碼,否則可能出現亂碼。
在撰寫程式時,儘量讓資料庫傳回 string
而非 byte[]
來避免手動解碼。如果不得已需要從資料庫取得位元組再轉字串,一定要知道那些位元組代表的編碼。例如:
// dataReader 是從資料庫讀取出來的 DataReader
byte[] blob = (byte[])dataReader["Name_Bytes"];
string name = Encoding.GetEncoding("Big5").GetString(blob);
如上,明確以 Big5 解碼才不會造成誤解碼。如果編碼不對,可能出現亂碼或問號。寫入時亦同理,應將字串轉成正確編碼的位元組再存入。如果資料庫與應用都使用 UTF-8/Unicode,則這方面的煩惱最少。因此最好的辦法還是保持端到端皆使用 Unicode,在資料庫層面就避免資訊遺失的風險。
跨平台文字處理
在跨平台環境下 (例如資料在 Windows 上產生,拿到 Linux 上處理),更需要小心編碼的一致。建議遵循以下原則:
-
使用通用編碼交換資料
如果可能,儘量使用 UTF-8 編碼來儲存和交換文字資料,因為它在各平台都是標準支援的 (.NET Core 預設就是 UTF-8)。
例如,你在 Windows 上產生一個 UTF-8 編碼的檔案,那麼在 Linux 或 macOS 上用 .NET 讀取時,不用做任何特殊設定就能正確解碼。
-
夾帶編碼訊息
在需要傳輸文字的場合,最好能夠隨資料附帶編碼說明。例如 XML/HTML 檔案應在檔頭宣告 encoding,或純文字檔可以考慮加上 BOM
(Byte Order Mark) 來提示。
雖然 BOM
不是強制的,但對於 UTF-8/UTF-16 檔案,Windows 上的某些軟體 (如舊版記事本) 會依賴 BOM 判斷編碼。如果沒有 BOM,確保協定或文件格式有其他方式知會編碼,否則另一端可能用錯編碼打開導致亂碼。
-
碼頁編碼的跨平台
如果必須交換非 Unicode 編碼的資料 (例如老系統產生了 Big5 檔案,要在 Linux .NET 上處理),務必在 .NET Core 環境安裝 System.Text.Encoding.CodePages
並 RegisterProvider
,否則程式無法識別 Big5 編碼。
好在只要提供器註冊了,在 Linux 下處理 Big5 與在 Windows 下效果是一樣的。因此,只要我們在跨平台程式中明確指定使用何種編碼讀寫,就能確保不同系統下結果相同。
簡而言之,跨平台時將文字以 Unicode (如 UTF-8) 形式交流是最穩妥的做法。如果必須使用其他編碼,也要讓接收方清楚知道使用何種編碼並具備相應支援。保持這種約定一致,才能避免「平台 A 寫的文件到平台 B 變亂碼」的窘境。
串流處理 (Stream)
除了檔案與網路,應用程式內也可能需要在串流中讀寫文字,例如讀寫 MemoryStream
、NetworkStream
等。使用 StreamReader
和 StreamWriter
配合 Encoding 可以方便地處理串流中的文字內容。用法上與檔案讀寫類似,只是改為對接任意的 Stream。
例如,假設有一段程式要經由網路串流傳送指令,接收端約定用 ASCII 編碼解析,那發送端可以這樣寫:
using NetworkStream ns = tcpClient.GetStream();
using StreamWriter writer = new StreamWriter(ns, Encoding.ASCII);
writer.Write("HELLO\n");
writer.Flush();
這會將字串「HELLO」按 ASCII 編碼寫入網路串流。接收端只要也按 ASCII 讀取就能得到正確的字元。再如,接收端想讀取 UTF-8 的訊息:
using StreamReader reader = new StreamReader(ns, Encoding.UTF8);
string message = reader.ReadLine();
StreamReader
/Writer
建構子允許我們指定編碼,還能指定是否需要偵測 BOM、緩衝大小等。若未指定編碼,StreamReader
預設行為與前述檔案情況一樣會當作 UTF-8 (含 BOM 偵測)。所以除非可以確定串流資料一定是 UTF-8,否則應明確給出編碼參數。
處理串流時,最好也考慮緩衝與 Flush 問題,例如上例中在寫入後呼叫了 Flush()
,確保資料立即從緩衝送出。另外,如果串流是雙向或長連線的,通信雙方應先約定好使用何種文字編碼來解析彼此的訊息,必要時可在協定中增加檔頭來協調 (類似 HTTP 的 Content-Type)。總之原則和先前一致:雙方編碼設定相符,傳輸內容才能正確還原。
最佳實踐與建議
綜合以上內容,以下是關於文字編碼在 .NET Core (含 .NET 5+) 開發中的一些最佳實踐建議:
-
預設使用 Unicode 編碼
優先選擇 UTF-8 或 UTF-16 作為應用程式處理和儲存文字的編碼。UTF-8 對英文內容幾乎沒有體積負擔,對全球語言都有支援,而且效能非常高。除非有特殊需求,UTF-8 通常是讀寫檔案、網路通訊的首選編碼。UTF-16 在 .NET 字串內部使用,可在需要固定雙位元組 (例如與某些 Windows API 互動) 時考慮。不建議使用 UTF-7 (已淘汰,安全性差) 等已過時的編碼。
-
減少不必要的編碼轉換
既然 .NET 字串本身就是 Unicode,應儘量讓資料以 Unicode 形式進出系統,避免在不同編碼之間來回轉換。比如從資料庫讀出 UTF-8 bytes -> 轉字串 -> 再轉成另一編碼 bytes 這種流程能免則免,直接在資料庫使用 Unicode 欄位即可省去轉換環節,減少出錯機率和效能開銷。
-
謹慎使用 ASCII/Ansi 編碼
僅在明確確認資料範圍時才使用 ASCII 或區域性碼頁編碼。例如只包含英數字元的通訊協定可使用 ASCII,但如果稍有可能出現非 ASCII 字元,應改用 UTF-8 以免資訊遺失。
實務中常見錯誤是誤用 ASCII 導致非英文字元全部變成問號 ?
。同理,除非處理特定舊系統資料,否則很少需要硬性使用 Big5、Shift-JIS 等編碼;即便需要,也應全程測試確保沒有字元轉換錯誤。
-
永遠明確指定編碼
無論是讀檔、寫檔,還是串流通訊,都養成指定 Encoding 的習慣,不要依賴預設值。這在跨區域、跨平台時尤為重要。例如 File.ReadAllText(path)
在 .NET Framework 上會用系統預設 ANSI 編碼讀檔案,在 .NET Core 則會用 UTF-8,行為不一致,而使用 File.ReadAllText(path, Encoding.X)
則可避免這種差異。
同樣,StreamReader/Writer
請提供 Encoding 參數。總之,將編碼視為檔案格式/通訊協定的一部分,跟檔名、副檔名一樣需要被明確管理。
-
啟用額外編碼提供器
對於需要處理非 Unicode 編碼資料的應用,例如批次轉換老舊文件編碼、讀取舊系統輸出報表等,請務必在程式初始化時呼叫 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance)
並隨專案一起部署 System.Text.Encoding.CodePages
套件。這樣可以避免某些環境下 GetEncoding
取不到所需編碼的問題。
一旦註冊,一些碼頁編碼就能跨平台使用,如 Big5 在 Linux 上也能正確轉換。
-
注意 Fallback (後備機制)
Encoding
在編碼 / 解碼遇到無法對應的字元時,預設會以「?
」取代未知字元。雖然這可避免拋出錯誤中斷程式,但有時可能掩蓋資料問題。如果對資料完整性要求很高,可以考慮設定 EncoderFallback/DecoderFallback 為 ExceptionFallback
,使其在遇到無法處理的字元時拋出例外,方便偵錯。此外也可客製替代字元。但大多數情況下,只要源和目標編碼對應正確,不應該出現亂碼或無法對應的情況。
-
測試與文件
最後,針對編碼相關的功能,多在不同語言環境下測試 (例如切換系統語言或在不同 OS 上測試),以發現潛在的問題。並且在文件或程式碼註解中標明資料的編碼假設,方便日後維護者了解。舉例而言,若你的應用匯出一份報表檔案是 Big5 編碼,務必在說明檔提及,否則接手的人可能困惑為何不用 UTF-8。
總而言之,文字編碼雖然看不見但無處不在。良好的習慣是:盡可能以 Unicode 處理文字,全程保持編碼一致,在需要轉換時使用 .NET 提供的 Encoding
工具類別並小心核對。
遵循這些最佳實踐,可以大幅降低遇到亂碼的機率,讓你的 .NET 應用在任何地區任何平台都能正確地讀寫文字資料。
相關連結