換行
換行(英語:newline、line ending、end-of-line (EOL)、line Feed (LF)、line break),在計算機領域中是加在一行文字最後位置的特殊字元,在換行字元的下一個字元將會出現在下一行。實際上換行字元根據不同的硬體平台或作業系統平台會有不同的編碼方式。
換行字符可以看作是行的結束符,也可以看作行之間的分隔符,這兩種處理方式之間存在一些歧義。如果換行字符被當作分隔符,那麼文件的最後一行就不需要再有換行字符。但是多數系統的做法是在最後一行的後面也加上一個換行字符,也就是把換行字符看作是行的結束符。這樣的程序在處理末行沒有換行字符的文件時,可能會存在問題。相反地,有的程序把換行符看作分隔符,就會把最末尾的換行字符看作是新行的開始,也就是多出了一個空行。
歷史
[編輯]早期計算機系統常使用電傳打字機作為控制台設備,需要使用CR+LF字符序列將打印機定位在新行的開頭。打印頭無法及時從最右邊返回到下一行的開頭來打印下一個字符。在CR之後打印的字符通常會在頁面中間打印為污跡,這是因為打印頭仍在移回行首位置的途中。「解決方案是將換行符設置為兩個字符:CR將打印頭回車移動到第一列,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),而不是回車轉義序列和換行轉義序列 '\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.