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