一行就是夾在兩個換行邊界之間的任何內容,而要在檔案內容中可靠地計算行數,最可靠的方法就是把文字貼到一個瀏覽器工具中,該工具會依據明確的邊界規則回報總行數、非空行數、空行數以及最長行的字元數。同一個檔案在兩個不同的程式會產生兩種不同的行數總計,原因幾乎都歸結於哪些字元被視為分隔符,以及是否納入末尾的空段落。LF(換行字元)、CRLF(歸位字元加上換行字元)以及單獨的 CR,各自都會構成一個邊界,而 CRLF 會被視為單一邊界,而不是兩個。只要存在任何字元,文字至少就包含一行,而位於最末尾的換行字元一定會在前面的內容之外再加上一個最終的空行。從「行數計算工具」得到的結果,正是依據這些規則與 Unicode 碼點(而非 UTF-16 程式碼單位、字素叢集或顯示欄位)來計算,所以同一段貼上的文字永遠會產生相同的數字。

count lines in file
以明確的邊界規則計算檔案中的行數

為什麼「計算檔案中的行數」在兩個工具中很少代表相同意思

大多數的行數計算差異來自三個地方:分隔符的選擇、對最後一段的處理方式,以及最長行的測量方式。Linux 指令可能計算換行字元,Windows 工具可能把檔案視為一連串記錄,文字編輯器可能會隱藏最後一個空行,而詩歌格式化工具則可能把詩節分段視為段落以外的其他東西。「行數計算工具」在這些問題上各採取一個立場並明確說明,讓你可以依據規則(而非某個特定程式)來比對其數字。如果你曾經把同一個檔案貼到兩個不同的計算工具中,卻得到兩個不同的答案,那麼原因幾乎一定是邊界規則。

如何計算檔案內容中的行數

最快的方法就是把文字拖進編輯器,然後讀取隨著你輸入或貼上而更新的四個數字。

  1. 在瀏覽器中開啟 行數計算工具。
  2. 把檔案內容貼到編輯器中,或直接輸入。不需要上傳或按送出按鈕。
  3. 讀取四項統計資料:總行數、非空行數、空白行或僅含空白字元的行數,以及以碼點計算的最長行。
  4. 驗證交叉檢查:非空行加上空行應等於總行數,對所有結果皆然。
  5. 在將該數字與其他工具進行比較前,先看一下結果下方所列出的換行與 Unicode 規則。

同一段輸入貼上兩次,在任何支援的瀏覽器中都會產生相同的數字,因為分析是一個確定性的本地函式。

邊界規則實際上是什麼樣子

行邊界是以下三者之一:LF (0x0A)、CRLF 配對 (0x0D 0x0A),或單獨的 CR (0x0D)。CRLF 被視為單一邊界,而不是兩個,所以在 Windows 上撰寫的檔案與在 Unix 上撰寫的檔案,只要可見的行內容相同,即使換行方式不同,只要結尾經過正規化,所產生的總行數仍會一致。混合的結尾方式可在同一段輸入中接受,這在文字曾於 Unix 殼層、Windows 剪貼簿以及經典 Mac 封存檔之間搬移時特別有用。

空輸入是唯一被定義為零行的情況。一旦編輯器中包含任何字元,文字就至少包含一行。在輸入最末尾的換行字元會在前面的內容之上再加上一個最終的空行,這條規則絕不會因為最後一個可見行是否恰好包含文字而被悄悄更改。

下方的四個情境展示了在這些規則下特定輸入如何被解析。最長行是以碼點計算,而不是以視覺欄位或位元組計算。

輸入總行數非空行空行最長行(碼點)
(空)0000
alpha1105
alpha + LF2115
僅 CRLF2020

在每一列中,非空行加上空行等於總行數,這是用來快速找出設定錯誤的工具或貼上過程中遺失字元的簡便方法。同樣的邏輯可以直接延伸:以兩個連續換行字元結尾的檔案,會產生一個非空的內容段落加上兩個結尾的空段落,因為每個換行邊界都會建立一個新的空白邊。

空行與非空行

當一行內容經過 JavaScript 的 trim 函式處理後回傳空字串時,該行即視為空行,這表示任何完全由空格、Tab 或其他 Unicode 空白字元組成的行都屬於空行。包含字母或數字的行是非空行,而混合字母與周圍空白的行也是非空行,因為 trim 後仍會留下至少一個可見字元。原始的空白並不會從輸入中被移除,並且仍會以碼點計入最長行,所以在一個原本為空的行上,三十個空格的縮排會被歸類為空行,即便其 30 個碼點仍會計入最長行長度。此分類純粹取決於 trim 之後是否仍有非空白字元存在。

總行數永遠是非空行與空行的總和,這讓你可以對編輯器所回報的每一個結果進行內建的交叉檢查。如果你的算術對不起來,首先要檢查的就是貼上的內容,因為智慧引號轉換或剪貼簿的自動縮排,可能在貼上過程中悄悄地新增或移除分隔符。

最長行以碼點計算,而不是以字元計算

「最長行」指標是以 Unicode 碼點計算,這是一個刻意的選擇,而不是預設行為。JavaScript 將字串儲存為 UTF-16 程式碼單位,當文字包含輔助字元時,兩者並不相同。例如一個 😀 表情符號佔用兩個 UTF-16 程式碼單位,但在「行數計算工具」的最長行指標中只算一個碼點。以零寬連接符串接的家庭表情符號,或由基底字元後接組合銳音符號所組成的帶重音字母,即使在視覺上可能呈現為單一符號或單一儲存格,實際上仍包含多個碼點。Tab 算作一個碼點,即使許多編輯器將其顯示為多個欄位。

這表示此指標並非該行在編輯器中的視覺寬度,也並非位元組長度,更不是使用者所感知到的字素叢集數量。「最長行(碼點)」這個標籤以及結果下方的補充說明,都是刻意撰寫以避免你將其誤讀為欄位計數。無論在哪個瀏覽器或工作階段中,該數字都會保持一致,因為分析是輸入中精確 UTF-16 程式碼單位的純本地函式。

在輸入上限時會發生什麼

編輯器最多接受 1,000,000 個 JavaScript UTF-16 程式碼單位,其測量方式與編輯器下方的字元計數器一致。恰好達到上限的文字會正常處理。如果輸入僅超過上限一個程式碼單位,整個分析就會回傳錯誤,所有先前的統計資料都會被清除,而且工具絕不會回傳部分計數或悄悄截斷的前段內容。此保護機制與最長行的碼點指標是分開的,所以單一輔助表情符號會消耗你兩個輸入程式碼單位,卻只貢獻一個碼點給最長行。

因為計算過程從不呼叫遠端服務,也從不上傳貼上的文字,所以同一段輸入在任何支援的瀏覽器中都會產生相同的統計資料,你可以視需要重複執行同一段貼上內容。「清除」按鈕會移除整段輸入,並將所有統計資料歸零,而不會影響頁面上的其他任何內容。

為什麼結果仍然可能與命令列工具不一致

對於同一個檔案,Linux 的 wc -l 與「行數計算工具」可能會回傳不同的總行數,差異幾乎都可追溯到三件事:邊界的定義方式、對最後一個換行的處理方式,以及最長行的指標。wc -l 指令計算的是換行字元的數量,所以一個不以換行字元結尾的檔案,在 wc -l 下會少算一行,但在「行數計算工具」中仍會登記為一整行。某些工具會在尾端換行之後隱藏最後的空段落,因此也會回報較小的數字。Windows 上的 PowerShell 若以記錄(而非分隔符字元)計算,則可能會回報第三個數字。

若你在 Windows 檔案與 Unix 管線之間移動,最安全的做法是先以「行數計算工具」對已知輸入進行計數,再把兩個數字與文件中說明的規則進行比對。基於瀏覽器的方式還有一個額外的好處,就是不受你目前所在作業系統的影響,這也是許多寫作者、記錄分析師以及程式碼審查者會在手上備一個本地計算工具的原因。

對於仍想要快速以殼層進行健全性檢查的使用者,在 Linux 中免用命令列輕鬆計算檔案行數一文會展示對應的 wc -l 與 PowerShell 指令,並同時列出邊界規則。只要同一個檔案處於同一種形式,這兩種方法的設計結果就會一致;但瀏覽器工具會把規則攤開來,讓不一致變得可以解釋,而不再是謎。

此工具不會做的事

「行數計算工具」刻意保持精簡。它不計算字數、句數、位元組數、段落數或字元數,也不產生帶編號的預覽,因為一百萬行的編號預覽要不是會讓頁面凍結,要不就是會產生一個看起來完整但實際上存在隱藏上限的結果。在文字區中視覺上換行的長行,只要文字中實際上不含 LF、CRLF 或 CR 分隔符,就仍然算是一個邏輯行。如果你需要與行數一起計算字數或字元數,相關的專用計算器可以接受同樣的貼上內容。此工具也不會對 Unicode 進行正規化、展開 Tab、移除不屬於分隔符一部分的歸位字元,或在分析之前以其他方式重寫你的文字。

正是這種精簡,使得邊界規則能用一段話清楚說明,並在每個結果中加以貫徹:在你的貼上內容與螢幕上的數字之間,並沒有任何正規化層。分類常式、最長行掃描以及輸入保護機制,讀取的都是同一份原始的 UTF-16 程式碼單位,這也正是同一段貼上內容在任何支援的瀏覽器中都會產生相同統計資料的原因。

若想進一步了解,請參閱《去除重複文字:實用的重複行移除指南》。