why newline at end of file




為什麼文本文件以換行符結束? (12)

我假設這裡的每個人都熟悉所有文本文件應該以換行符結束的格言。 我已經知道這個“規則”多年了,但我總是想知道 - 為什麼?


為什麼(文本)文件以換行符結束?

很多人也表示,因為:

  1. 許多程序行為不好,或沒有它就失敗。

  2. 即使是處理文件的程序也沒有結尾'\n' ,該工具的功能可能無法滿足用戶的期望 - 在這種情況下可能不清楚。

  3. 程序很少不允許最終的'\n' (我不知道任何)。

然而,這引出了下一個問題:

代碼應該如何處理沒有換行的文本文件?

  1. 最重要的 - 不要編寫假定文本文件以換行符結尾的代碼假設文件符合格式導致數據損壞,黑客攻擊和崩潰。 例:

    // Bad code
    while (fgets(buf, sizeof buf, instream)) {
      // What happens if there is no \n, buf[] is truncated leading to who knows what
      buf[strlen(buf) - 1] = '\0';  // attempt to rid trailing \n
      ...
    }
    
  2. 如果需要最後的尾部'\n' ,請提醒用戶缺席並採取措施。 IOWs,驗證文件的格式。 注意:這可能包括對最大行長度,字符編碼等的限制。

  3. 明確定義,文件,代碼處理缺失的最終'\n'

  4. 盡可能不要生成缺少結尾'\n'


一些工具期望這一點。 例如, wc期望:

$ echo -n "Line not ending in a new line" | wc -l
0
$ echo "Line ending with a new line" | wc -l
1

假設文件正在被另一個進程生成時正在處理文件。

這可能與此有關? 表示文件已準備好處理的標誌。


因為這就是POSIX標准定義一條線的方式

3.206線
一系列零個或多個非<新行>字符加上終止的<新行>字符。

因此,不以換行符結尾的行不被視為實際行。 這就是為什麼有些程序在處理文件的最後一行時遇到問題,如果它不是換行符終止。

在終端仿真器上工作時,本指南至少有一個硬件優勢:所有的Unix工具都希望遵循這個慣例並且能夠使用它。 例如,當與cat連接文件時,由換行符終止的文件將具有不同於不具有以下特徵的文件:

$ more a.txt
foo$ more b.txt
bar
$ more c.txt
baz
$ cat *.txt
foobar
baz

而且,如前面的例子所示,當在命令行上顯示文件時(例如,通過more ),以換行符結尾的文件會導致正確的顯示。 不正確地終止的文件可能會出現亂碼(第二行)。

為了保持一致性,遵循這條規則非常有幫助 - 否則在處理默認的Unix工具時會招致額外的工作。

現在,在不符合POSIX標準的系統(現在主要是Windows)上,重點是沒有意義的:文件通常不以換行符結尾,而行的(非正式)定義可能是“由換行符分隔的文本” (注意重點)。 這完全有效。 但是,對於結構化數據(例如編程代碼),它使解析最小化更複雜:通常意味著解析器必須被重寫。 如果解析器最初是用POSIX定義編寫的,那麼修改令牌流而不是解析器可能更容易 - 換句話說,在輸入的末尾添加“artificial newline”標記。


多年來我一直在想這個。 但今天我遇到了一個很好的理由。

想像一下每行都有記錄的文件(例如:一個CSV文件)。 並且計算機在文件末尾寫入記錄。 但它突然墜毀。 Gee是最後一條線? (不是很好的情況)

但是如果我們總是終止最後一行,那麼我們會知道(簡單地檢查最後一行是否終止)。 否則,為了安全起見,我們可能不得不放棄最後一行。


恕我直言,這是個人風格和意見的問題。

在過去的一段時間裡,我沒有放過那條新線。 保存的字符意味著通過該14.4K調製解調器更快的速度。

後來,我把這個換行符放在最後,使用shift + downarrow來選擇最後一行。


我個人喜歡源代碼文件末尾的新行。

它可能與Linux或所有UNIX系統有關。 我記得那裡有編譯錯誤(如果我沒有弄錯,gcc),因為源代碼文件沒有以一個空的新行結束。 為什麼這樣做讓人想知道。


據推測,只是一些解析代碼預計它在那裡。

我不確定我會認為這是一個“規則”,它當然不是我堅持宗教的東西。 大多數敏感的代碼將知道如何逐行解析文本(包括編碼)(任何行結束的選擇),在最後一行上帶或不帶換行符。

事實上 - 如果你以一條新線結束:是否存在(理論上)EOL和EOF之間的最後一條線? 一個思考...


這個答案是一個技術答案而不是意見的嘗試。

如果我們想成為POSIX純粹主義者,我們將一條線定義為:

一系列零個或多個非<新行>字符加上終止的<新行>字符。

來源: http : //pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206

不完整的行如下所示:

文件末尾的一個或多個非<新行>字符序列。

資料來源: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195 : http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195

一個文本文件為:

包含組織為零或多行的字符的文件。 這些行不包含NUL字符,並且任何長度都不能超過{LINE_MAX}個字節,包括<newline>字符。 儘管POSIX.1-2008沒有區分文本文件和二進製文件(請參閱ISO C標準),但許多實用程序僅在文本文件上操作時才會產生可預測或有意義的輸出。 具有此類限制的標準實用程序始終在STDIN或INPUT FILES部分中指定“文本文件”。

資料來源: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_397 : http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_397

一個字符串如下:

由第一個空字節終止的連續字節序列。

來源: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396

從此,我們可以推導出,我們唯一可能遇到的任何類型的問題是,如果我們將文件行或文件的概念作為文本文件處理 (即文本文件是零組織或更多的行,我們知道的一行必須以<newline>結尾)。

例如: wc -l filename

wc的手冊中我們看到:

一行被定義為由<換行符>字符分隔的一串字符。

對JavaScript,HTML和CSS文件有什麼影響,然後是文本文件?

在瀏覽器,現代IDE和其他前端應用程序中,在EOF上跳過EOL沒有任何問題。 應用程序將正確解析文件。 因為並非所有操作系統都符合POSIX標準,所以對於非操作系統工具(例如瀏覽器)根據POSIX標準(或任何操作系統級標準)來處理文件是不切實際的。

因此,我們可以相對確信EOF在應用程序級別上幾乎沒有負面影響 - 無論它是否在UNIX操作系統上運行。

在這一點上,我們可以自信地說,在客戶端處理JS,HTML和CSS時,在EOF上跳過EOL是安全的。 實際上,我們可以聲明縮小這些文件中不包含<newline>的文件是安全的。

我們可以進一步說,就NodeJS而言,它也不能遵守POSIX標準,因為它可以在非POSIX兼容環境中運行。

那麼我們留下了什麼? 系統級工具。

這意味著唯一可能出現的問題是使用努力將其功能與POSIX的語義相結合的工具(例如wc所示的行的定義)。

即便如此,並非所有的shell都會自動遵守POSIX。 Bash例如不默認為POSIX行為。 有一個開關來啟用它: POSIXLY_CORRECT

關於EOL的價值的思考<newline>: http://www.rfc-editor.org/EOLstory.txt : http://www.rfc-editor.org/EOLstory.txt

留在模具軌道上,為了所有的實際意圖和目的,讓我們考慮一下:

我們來處理一個沒有EOL的文件。 在撰寫本文時,本例中的文件是一個沒有EOL的縮小JavaScript。

curl http://cdnjs.cloudflare.com/ajax/libs/AniJS/0.5.0/anijs-min.js -o x.js
curl http://cdnjs.cloudflare.com/ajax/libs/AniJS/0.5.0/anijs-min.js -o y.js

$ cat x.js y.js > z.js

-rw-r--r--  1 milanadamovsky   7905 Aug 14 23:17 x.js
-rw-r--r--  1 milanadamovsky   7905 Aug 14 23:17 y.js
-rw-r--r--  1 milanadamovsky  15810 Aug 14 23:18 z.js

請注意, cat文件大小恰好是其各個部分的總和。 如果JavaScript文件的連接是JS文件的關注點,則更合適的關注點是使用分號開始每個JavaScript文件。

就像其他人在這個線程中提到的那樣:如果你想要cat兩個文件,其輸出只是一行而不是兩行? 換句話說, cat做它應該做的事情。

catman只提到閱讀輸入到EOF,而不是<newline>。 請注意, cat-n開關也會將非終止行(或不完整行 )打印出一行 - 即計數從1開始(根據man

-n從1開始對輸出行進行編號。

現在我們已經理解POSIX如何定義一條 ,這種行為變得模糊不清或者確實不符合規定。

了解給定工具的目的和合規性將有助於確定使用EOL結束文件的重要性。 在C,C ++,Java(JAR)等...一些標準將決定新的有效性 - 沒有這樣的JS,HTML,CSS標準。

例如,不用wc -l filename就可以awk '{x++}END{ print x}' filename ,並確信任務的成功不會受到我們可能想要處理的文件的危害,我們沒有寫(例如第三方庫,比如我們curl的縮小的JS) - 除非我們的意圖是在POSIX合規意義上真正地計數

結論

對於某些文本文件(如JS,HTML和CSS),在EOF中跳過EOL將產生負面影響 - 如果有的話,實際使用情況很少。 如果我們依賴<newline>存在,我們只會將我們的工具的可靠性限制在我們編寫的文件中,並將自己置於由第三方文件引入的潛在錯誤之中。

故事的道德:在EOF上沒有依賴EOL的弱點的工程師工具。

隨意發布使用案例,因為它們適用於JS,HTML和CSS,我們可以檢查如何跳過EOL會產生不利影響。


這可能與以下兩者之間差異有關:

  • 文本文件(每行應該在行尾結束)
  • 二進製文件(沒有真正的“行”說,文件的長度必須保留)

例如,如果每行都在行尾結束,則可避免將兩個文本文件連接起來,從而使第一次運行的最後一行進入第二行的第一行。

此外,編輯器可以在加載時檢查文件是否在行尾結束,將其保存在本地選項“eol”中,並在寫入文件時使用該選項。

幾年前(2005年),許多編輯(ZDE,Eclipse,Scite,...)沒有“忘記”最終的EOL, 這並不是非常讚賞
不僅如此,他們錯誤地將最終的EOL解釋為“開始新的一行”,並且實際上開始顯示另一行,就好像它已經存在一樣。
這是非常明顯的與一個像vim一樣行為良好的文本編輯器的“正確的”文本文件,而不是在上面的編輯器中打開它。 它在文件的最後一行下面顯示了一行額外的行。 你看到這樣的事情:

1 first line
2 middle line
3 last line
4

還有一個實際的編程問題,最後缺少新行的文件:內置read Bash(我不知道其他read實現)沒有按預期工作:

printf $'foo\nbar' | while read line
do
    echo $line
done

這只打印foo ! 原因是當read遇到最後一行時,它將內容寫入$line但由於它到達EOF而返回退出代碼1。 這打破了while循環,所以我們永遠不會到達echo $line部分。 如果你想處理這種情況,你必須做到以下幾點:

while read line || [ -n "${line-}" ]
do
    echo $line
done < <(printf $'foo\nbar')

也就是說,如果由於文件結尾處的非空行而導致read失敗,請執行echo 。 當然,在這種情況下,輸出中會有一個額外的換行符不在輸入中。


除了上面的實際原因外,如果Unix的創始人(Thompson,Ritchie等)或他們的Multics前輩認識到使用行終止符而不是行分隔符有理論上的理由,終止符,你可以編碼所有可能的文件的行。 使用行分隔符,零行文件和包含單個空行的文件之間沒有區別; 它們都被編碼為包含零個字符的文件。

所以,原因是:

  1. 因為這是POSIX定義它的方式。
  2. 因為有些工具期望它或沒有它的“行為不端”。 例如,如果不以換行符結束, wc -l將不會計算最終的“行”。
  3. 因為它簡單方便。 在Unix上, cat只是起作用而且沒有復雜性。 它只是複制每個文件的字節,而不需要解釋。 我不認為有一個相當於cat的DOS。 使用copy a+bc將最終合併文件a的最後一行和文件b的第一行。
  4. 因為零線的文件(或流)可以與一個空行的文件區分開來。






newline