跨平台開發最讓人頭痛的細節之一,就是換行符不一致。Windows 用 \r\n (CRLF),Linux/Unix/macOS 用 \n (LF),舊版 Mac OS 9 用 \r (CR)。這導致 Git diff 雜訊、編譯警告、腳本執行失敗、協議解析錯誤等無數問題。本文徹底釐清來龍去脈與解決之道。
| 符號 | 名稱 | ASCII (Hex) | 十進位 | 功能 |
|---|---|---|---|---|
\n | Line Feed (LF) | 0x0A | 10 | 換行 |
\r | Carriage Return (CR) | 0x0D | 13 | 歸位 (游標移至行首) |
| 平台 | 鍵盤 Enter 產生 | 文字檔儲存 | C 語言 \n 行為 | Notepad 顯示 |
|---|---|---|---|---|
| Windows | \r\n | \r\n (0x0D 0x0A) | 輸出 \n → 實際寫入 \r\n | 需 \r\n 才換行 |
| Linux/Unix | \n | \n (0x0A) | 輸出 \n → 寫入 \n | N/A |
| macOS (現代) | \n | \n (0x0A) | 輸出 \n → 寫入 \n | N/A |
#include <stdio.h>
int main() {
char a[10] = "abc\r";
printf(a); // 無任何輸出!
return 0;
}
原因:\r 將游標移回行首,後續輸出覆蓋 "abc"。若接著 printf("def"),螢幕顯示 "def"。
fopen("file.txt", "r") / "w"
\r\n → \n;寫入時 \n → \r\nfopen("file.bin", "rb") / "wb"
建議:處理文字檔用文字模式,處理二進位檔 (圖片、壓縮檔、協議封包) 必須用二進位模式。
# .gitattributes (專案根目錄)
* text=auto eol=lf
# 或全域設定
git config --global core.autocrlf input # 提交時轉 LF,簽出不轉
"files.eol": "\n":set fileformat=unix// 讀取整行並移除尾部換行符 (跨平台安全)
void read_line(FILE *fp, char *buf, size_t size) {
if (fgets(buf, size, fp)) {
size_t len = strlen(buf);
while (len > 0 && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
buf[--len] = '\0';
}
}
}
// 通用字串分割:支援 \n, \r\n, \r
char **split_lines(const char *text, int *count) {
// 以 \r 或 \n 為分隔符,忽略空行...
}
RFC 規定必須使用 \r\n (CRLF) 作為行終結符,無論平台。發送時請硬編碼 "\r\n" 或使用常數 CRLF。
| 工具 | CRLF → LF | LF → CRLF |
|---|---|---|
| dos2unix / unix2dos | dos2unix file.txt | unix2dos file.txt |
| sed | sed -i 's/\r$//' file | sed -i 's/$/\r/' file |
| tr | tr -d '\r' < in > out | awk '{print $0 "\r"}' < in > out |
| Python | open('in', newline='').read() | open('out', 'w', newline='\r\n') |
換行符雖小,折騰不少。核心原則三點:**倉庫統一 LF、編輯器配合、二進位檔案禁用文字模式**。理解歷史背景與平台差異,才能在跨平台協作中從容應對,不再為「多一個 \r」或「少一個 \n」煩惱。