編譯 | 蘇宓
作為一名程序員,想必你對 CRLF 并不陌生。
CRLF,全稱 Carriage Return Line Feed,中文翻譯為回車換行。它由兩個字符組成:CR (\r,回車) 和 LF (\n,換行),其中回車是將光標移動到當前行的最左側,而換行則是將光標下移一行。這里還需要提及的一個概念是——新行 (NL,NewLine),它是指將光標下移一行,并移動到當前行的最左側。CRLF 的存在主要是為了兼容不同操作系統的文件格式。通常,Windows 使用 CRLF 作為換行符,而 Unix/Linux 和 macOS 只使用 LF。然而,在實際開發中,CRLF 和 LF 的差異常常導致不少開發團隊在處理文件時出現令人頭疼的編碼錯誤和沖突。越來越多的開發者認為:CRLF 已經過時,應該被直接廢除。這個觀點引發了不少爭議,但也讓人開始重新思考:在現代開發環境中,我們是否真的需要繼續支持 CRLF?
這則聲明由美國軟件開發者 D. Richard Hipp 發起的,他不僅創建了 SQLite 開源嵌入式關系數據庫,也開發了分布式版本控制系統 Fossil 和網絡服務器 Althttpd 等軟件。D. Richard Hipp 表示,“回車”和“新行”都是有用的控制字符。NL(新行)是最常見的操作,表示開始新的一行并從行首開始寫。單獨的 CR 有時也有用,尤其當你想覆蓋已經寫好的文字時。而 LF(換行)基本上沒什么用。沒有人希望在一行的中間停止,然后向下移動一行并從同一列繼續寫。沒有任何實際程序會這么做。LF 之所以存在,是在計算機終端還是電傳打印機的時候遺留下來的東西。
D. Richard Hipp 稱,LF 誕生于大約 70 年前的機械電傳打字機時代。當時的電傳打字機沒有使用晶體管,而是完全由齒輪、凸輪、馬達、繼電器和伺服裝置組成的。它們非常神奇,可以將通過兩根銅線傳輸的二進制代碼轉換為紙上的打印文本。電傳打字機就像是普通打字機一樣工作,每秒打印大約 5 個字符。打印頭是一個包含字母的圓柱體或橢圓形小球。打印頭與紙之間有一個浸有墨水的布帶。為了打印一個字符,打印頭會旋轉到正確的位置,然后向前撞擊,使布帶上的墨水在紙上形成所需字符的形狀。每打印一個字符,整個打印頭機構(小球、墨帶以及各種控制凸輪和齒輪)都會向右移動一個字符的位置。這一切每秒發生五次。這些機器運作時噪音很大,并且會明顯震動。在一行文本的末端,打印頭必須返回到最左側。打印頭移動得很快,但移動到最左邊仍需要時間。當時沒有內存,所以打印頭必須在下一個字符到來之前完全移到左邊。為了實現這一點,NL(新行)操作被分為兩個子操作:CR(回車)和 LF(換行)。CR(回車)先行,啟動打印頭向左移動。當打印頭還在移動時,LF(換行)會到達,導致紙張滾動一行。這個額外的 LF(換行)字符為打印頭爭取到足夠的時間,在下一個字符到達之前完全移到最左側。回看過去,文本行以 CRLF 結尾的傳統可以追溯到 20 世紀 50 年代電傳打字機的機械限制。這是一個典型的例子,說明底層實現的細節如何暴露在用戶界面中。到 20 世紀 60 年代末和 70 年代初的 Multics 和 Unix 時代,大多數人意識到使用 CRLF 作為 NL(新行)是沒有意義的。因此,發送單獨的 CR 和 LF 字符的任務被移交給了電傳打字機的設備驅動程序,因為硬件缺陷的解決應該在驅動程序層處理。計算機只需保存一個 NL(新行)字符,并采用與電傳打字機相同的 LF(換行)代碼來表示 NL,這里真正的 LF 在實際應用中沒有任何意義,因此它的數字代碼被重新用于表示 NL。如今,CR 用 Unicode 編碼中的 U+000d 作為代碼點來表示,LF 和 NL 都用 U+000a 表示。幾乎所有現代機器都僅使用 U+000a 表示 NL,這個意義也嵌入在大多數編程語言中,通常使用反斜杠轉義符 \n。盡管如此,仍有少數機器堅持在 NL 之前發送 CR,而 U+000a 的官方 Unicode 名稱仍然是 LF。此外,一些協議(如 HTTP、SMTP、CSV)仍然“要求”每行以 CRLF 結尾。如今幾乎所有軟件都會接受單獨的 NL 字符(沒有前置 CR)來表示行結束。你必須非常仔細地尋找,才能找到真正將 U+000a 解釋為換行的設備或應用程序。“這一傳統,即在每個 NL(新行)之前發送無用的 CR(回車),起源于旋轉撥號電話時代,甚至是在集成電路發明之前。這種做法在我們的現代世界中已經沒有理由繼續存在了。額外的 CR 沒有任何實際用途,只是給程序員帶來不必要的麻煩,浪費帶寬。”
在這樣的背景下,D. Richard Hipp 發起呼吁——「所有追求簡潔、和平,并希望促進人類繁榮的人,請與我一起反對使用 CRLF,幫助它迅速成為歷史的遺跡。」- 停止將“linefeed”(LF)作為 U+000a 代碼點的名稱。過去二十年內構建的大多數技術,以及過去半個世紀的大多數技術,已經將 U+000a 理解為“newline”(新行)而不是“linefeed”(換行)。雖然“linefeed”是它的歷史名稱,但那又有什么關系呢?在幾乎所有實際應用中,它表示的是“新行”,因此請直接稱它為“newline”。
- 停止發送不必要的 CR(回車)。僅在你確實需要用新內容覆蓋當前行時才使用 CR。在 NL(新行)之前加上 CR 完全是浪費帶寬。除非你必須與某些頑固的系統通信,而這些系統執意停留在 1950 年代,否則不要在 NL 前加上 CR。
- 即使某些現有協議(如 HTTP、SMTP、CSV、FTP)技術上要求以 CRLF 作為行尾,也不要遵從。只發送 NL。盡管技術上不正確,但幾乎所有這些協議的實現都會接受單獨的 NL 作為行尾標記。不要屈服于 CRLF 的控制。
- 修復那些在接收到沒有前置 CR 的 NL 時表現異常或報錯的軟件。所有現代軟件都應接受單獨的 U+000a 字符作為有效的行尾標記。系統可能會為了向后兼容而接受 CR 加 NL,但要求 CR 加 NL 的軟件是有問題的。
在 D. Richard Hipp 看來,CRLF 的終結早該到來了,因為它早就過時了。
?
萬萬沒想到,此話一出,引發程序員強烈的共鳴,同時也有不少人持有不同的看法。有開發者稱,「我完全同意。這會導致無盡的混亂,尤其是在跨平臺文本文件中。更不用說以編程方式解析了。」不過,也有來自 HN 上的網友 forrestthewoods 表示:簡單來說——別抱怨,自己解決。處理不同或混合的行結尾確實是個輕微的小麻煩,但并不復雜或困難。不要為了讓自己輕松一點就讓別人承擔不必要的麻煩。接受它,繼續前行。@Animats 認為,「與其呼吁,倒不如說服微軟。因為這正是 DOS 遺留導致讓這一切得以延續」。@bmitc 稱:除了那些設計糟糕的 Unix 工具和 Git,誰還會被這種問題困擾?為了適應 Linux,我將編輯器配置為無論在哪個操作系統上都使用 LF,并確保 Git 不再混淆行結尾。在處理串行協議時,我從來沒有遇到過問題。隨著爭議的發酵,D. Richard Hipp 迫于無奈之下,于今日更新了自己的聲明,他表示:看起來(1)目前主流中的軟件依賴于過時的 CRLF 行結尾的數量比我原先預想的還要多;(2) 很多人并不認同我創建一個無 CRLF 世界的熱情。唉,這讓我有些失望,但現實就是如此。感謝所有愿意嘗試這個想法的人,本來幾乎成功了!因此,我在此撤回這項提議,并已將我所有的系統恢復為在規范要求的情況下生成 CRLF。真是遺憾。不過,這次實驗帶來一個意外的好處,我在 Fossil 和 althttpd 中發現并修復了一些問題,這些程序此前要求必須使用 CRLF,且不允許使用單獨的 NL 作為替代。
來源:
https://fossil-scm.org/home/ext/crlf-harmful.md
https://news.ycombinator.com/item?id=41830717
該文章在 2024/10/16 10:20:35 編輯過