文字模式 vs 二進位模式
對待一個檔案有兩種方式:當成給人讀的純文字散文,或當成一串原始位元組、其意義由你的程式決定。這個差別看似隱形——直到牽涉到換行符時——然後它就能悄悄毀掉你的資料。文字模式讓函式庫替你整理換行符;二進位模式則說「別碰,把位元組原封不動給我」。
具體來說,問題在於換行符如何表示。類 Unix 系統用單一位元組來結束一行,即換行(LF,0x0A)。Windows 用兩個位元組來結束一行,即歸位再換行(CR LF,0x0D 0x0A)。當你以文字模式開啟一個 stdio 串流時,Windows 上的 C 函式庫會在兩者間轉換:讀取時它去掉 CR,使你的程式只看到 LF;寫入時它在每個 LF 前加上一個 CR。這對文字方便,但對非文字資料是場災難,因為一個剛好等於 0x0D 或 0x0A 的位元組是真實資料、不是換行符——悄悄插入或移除 CR 位元組會毀掉檔案。二進位模式(fopen 模式中的 "b",像 "rb" 與 "wb")關掉這個轉換:每個位元組進去與出來都與儲存時一模一樣。在類 Unix 系統上,文字模式與二進位模式完全相同(沒有轉換要做),這正是為什麼 Unix 程式設計師常忘了這個區別存在——直到他們的程式碼跑在 Windows 上。
為何重要:這是個經典的跨平台陷阱。一個在 Windows 上以文字模式讀取影像或編譯檔的程式,會把它弄壞,在每個 0x0A 周圍刪掉或加上位元組。規則很簡單:凡不是以行為單位的人類文字,就以二進位模式開啟。而即便在文字之中,LF 與 CRLF 的不一致,也正是為什麼一個在某系統上編輯的檔案,到另一系統上可能看起來有怪異的 ^M 字元,或整個擠在「同一行」——不同的是換行符慣例,而非內容。
FILE *img = fopen("photo.png", "rb"); /* 二進位:位元組原封不動地出來 */ FILE *txt = fopen("notes.txt", "r"); /* 文字:在 Windows 上會做 CRLF <-> LF 轉換 */
任何非文字檔案都用 "rb"/"wb";在文字模式下 Windows 會在你不知情時改寫換行符。
在類 Unix 系統上文字模式與二進位模式完全相同,所以這個臭蟲只在你的程式碼跑到 Windows 時才現身。換行符在 Unix 是 LF(0x0A),在 Windows 是 CRLF(0x0D 0x0A);絕不要以文字模式開啟二進位資料。