換行
換行(英語:newline、line ending、end-of-line (EOL)、line Feed (LF)、line break),在計算機領域中是加在一行文字最後位置的特殊字元,在換行字元的下一個字元將會出現在下一行。實際上換行字元根據不同的硬件平台或作業系統平台會有不同的編碼方式。
換行字元可以看作是行的結束符,也可以看作行之間的分隔符,這兩種處理方式之間存在一些歧義。如果換行字元被當作分隔符,那麼檔案的最後一行就不需要再有換行字元。但是多數系統的做法是在最後一行的後面也加上一個換行字元,也就是把換行字元看作是行的結束符。這樣的程式在處理末行沒有換行字元的檔案時,可能會存在問題。相反地,有的程式把換行符看作分隔符,就會把最末尾的換行字元看作是新行的開始,也就是多出了一個空行。
歷史
早期電腦系統常使用電傳打字機作為控制台裝置,需要使用CR+LF字元序列將印表機定位在新行的開頭。列印頭無法及時從最右邊返回到下一行的開頭來列印下一個字元。在CR之後列印的字元通常會在頁面中間列印為污跡,這是因為列印頭仍在移回行首位置的途中。「解決方案是將換行符設置為兩個字元:CR將列印頭Enter移動到第一列,LF將紙張向上移動。」[1]事實上,通常需要傳送額外的但會被忽略的字元(無關的CR或 NUL)以給列印頭更多時間移動到左邊距。
MS-DOS(1981年)為了相容採用了遵循DEC小型機標準的CP/M的CR+LF,而這個約定也被微軟後來的Windows作業系統繼承了。
1964年開始開發的Multics作業系統使用單獨的LF作為換行符。Multics使用裝置驅動程式將LF字元轉換為印表機所需的任何字元序列(包括額外的填充字元),且單位元組更便於編程。CR字元提供了將一行與另一行疊印以建立粗體、底線和刪除線效果的有用功能。單獨使用LF作為行終止符已經被納入最終的ISO/IEC 646標準草案中。Unix遵循了Multics的做法,後來類Unix系統也遵循了Unix。 這在Windows和類Unix作業系統之間造成了衝突,在一個作業系統上編寫的檔案無法正確格式化或由另一個作業系統解釋。
表示
程式語言
printf("Hello world!\n");
Unicode
Unicode標準指定以下的字元為相容標準的應用程式應辨識的換行字元:[2]
- LF: 換行,U+000A
- VT: 垂直定位,U+000B
- FF: 換頁符,U+000C
- CR: 回車字元,U+000D
- CR+LF:CR(U+000D)後跟LF(U+000A)
- NEL: 下一行,U+0085
- LS: 分行,U+2028
- PS: 分段,U+2029
程式語言支援
為了便於建立可移植程式,程式語言提供了一些抽象來處理不同環境中使用的不同類型的換行序列。
C語言提供跳脫序列「\n」(換行符)和「\r」(回車字元)。但是,這些字元不需要等同於ASCII編碼中的LF和CR控制字元。C語言標準只保證兩件事:
- 這些跳脫序列中的每一個都對映到一個唯一的實現定義的數字,該數字可以作為單個字元值儲存。
- 當以文字模式寫入檔案、裝置節點或socket/fifo時,「\n」會透明地轉換為系統使用的本機換行序列,該序列可能比一個字元長。在文字模式下讀取時,本機換行符序列將轉換回 '\n'。在二進制模式下,不進行任何翻譯,直接輸出'\n'產生的內部表示。
在C起源的Unix平台上,本機換行序列是ASCII LF (0x0A),因此 '\n' 被簡單地定義為該值。由於內部和外部表示相同,因此在文字模式下執行的翻譯是無操作的,並且Unix沒有文字模式或二進制模式的概念。這導致許多在Unix系統上開發軟件的程式設計師完全忽略了這種區別,導致代碼無法移植到不同的平台。
最好避免在二進制模式下使用C標準庫函數fgets(),因為任何未使用Unix換行符約定寫入的檔案都將被誤讀。 另外,在文字模式下,任何未使用系統本機換行序列寫入的檔案(例如在 Unix 系統上建立的檔案,然後複製到 Windows 系統)也會被誤讀。
另一個常見問題是在使用強制使用ASCII CR+LF 來結束行的Internet協定進行通訊時使用「\n」。將 '\n' 寫入文字模式流在Windows系統上可以正常工作,但在Unix上僅產生LF,而在更奇特的系統上則完全不同。在二進制模式下使用「\r\n」稍好一些。
許多語言,例如C++、Perl、Haskell都提供與C語言相同的 '\n' 解釋。C++有另一種I/O模型,其中操縱器std::endl可用於輸出換行符(並重新整理流緩衝區)。
Java、PHP和Python提供 '\r\n' 序列(對應於ASCII的CR+LF)。與C不同,它們保證分別表示值U+000D和U+000A。
Java I/O庫不會透明地將這些轉換為輸入或輸出上依賴於平台的換行序列。 相反,它們提供了用於寫入自動添加本機換行序列的整行的函數,以及用於讀取接受CR、LF或CR+LF作為行終止符的行的函數。System.lineSeparator()方法可用於檢索底層行分隔符。
C#例子:
string eol = Environment.NewLine;
string lineColor = "Color: Red" + eol;
string eol2 = "\n";
string lineColor2 = "Color: Blue" + eol2;
不同換行符格式帶來的問題
不同的換行約定會導致在不同類型的系統之間傳輸的文字檔案顯示不正確。
使用類Unix或經典Mac OS上常見的程式建立的檔案中的文字,在MS-DOS和Microsoft Windows常見的大多數程式中顯示為單長行,因為這些程式不把單個換行符或單個回車字元顯示為換行。
相反,在類Unix系統上檢視源自Windows電腦的檔案時,額外的回車字元可能會顯示為第二個換行符、^M或每行末尾的<cr>。
此外,文字編輯器以外的程式可能不接受檔案,例如一些設定檔,使用外部換行約定編碼視作有效檔案。
這個問題可能很難發現,因為有些程式可以正確處理外部換行符,而另一些則不能。例如,即使原始檔在控制台或編輯器中顯示時看起來正確,編譯器也可能會因模糊語法錯誤而失敗。現代文字編輯器通常可以辨識所有類型的CR+LF換行符,並允許用戶在不同標準之間進行轉換。Web瀏覽器通常還能夠顯示使用不同類型換行符的文字檔案和網站。
即使程式支援不同的換行符約定,這些功能通常也沒有得到充分的標記、描述或記錄。通常,將向用戶顯示列舉不同換行符約定的選單或組合方塊,而不指示該選擇是否將重新解釋、臨時轉換或永久轉換換行符。有些程式會在打開、複製、貼上或儲存時隱式進行轉換。
大多數文字Internet協定包括 HTTP、SMTP、FTP、IRC 等)都要求在協定級別使用ASCII的CR+LF('\r\n', 0x0D 0x0A),但建議寬容的應用程式辨識單獨的LF ('\n', 0x0A) 也視作換行。儘管有規定的標準,許多應用程式錯誤地使用C語言換行跳脫序列 '\n' (LF),而不是Enter跳脫序列和換行跳脫序列 '\r\n' (CR+LF) 的正確組合。 當嘗試與遵循更嚴格的標準解釋而不是建議的寬容解釋的系統進行通訊時,這種意外使用錯誤的跳脫序列會導致問題。qmail郵件傳輸代理就是這樣一種不寬容的系統,它主動拒絕接受來自傳送裸LF而不是所需CR+LF的系統的訊息。[3]
電子郵件的標準互聯網訊息格式[4]規定:「CR和LF必須僅作為CRLF一起出現;它們不得獨立出現在正文中」。
當傳輸以「ASCII模式」完成時,檔案傳輸協定可以自動轉換在具有不同換行符表示的系統之間傳輸的檔案中的換行符。然而,以這種模式傳輸二進制檔案通常會產生災難性的結果:任何出現的換行符位元組序列(在此上下文中沒有行終止符語意,但只是正常位元組序列的一部分)都將被轉換為任何換行符表示形式其他系統使用,實質上損壞了檔案。FTP客戶端通常採用一些啟發式方法(例如,檢查副檔名)來自動選擇二進制或 ASCII 模式,但最終由用戶來確保其檔案以正確的模式傳輸。如果對正確模式有任何疑問,應使用二進制模式,因為這樣FTP不會更改任何檔案,儘管它們可能顯示不正確。[5]
相關條目
參考資料
- ^ Qualline, Steve. Vi Improved - Vim (PDF). Sams Publishing. 2001: 120 [4 January 2023]. ISBN 9780735710016. (原始內容 (PDF)存檔於8 April 2022).
- ^ Unicode Standard Annex #14 UNICODE LINE BREAKING ALGORITHM. [2014-05-01]. (原始內容存檔於2021-03-08).
- ^ Bernstein, D.J. Bare LFs in SMTP. [2023-08-19]. (原始內容存檔於2011-10-29).
- ^ Resnick, Pete. Internet Message Format. April 2001 [2023-08-19]. RFC 2822(原出處存檔於2016-06-13).
- ^ File Transfer. (原始內容存檔於2016-05-14).
When in doubt, transfer in binary mode.