從 PDF 複製一段文字貼到別的地方,它常常被拆成一堆短行;貼一份清單,又有一半的列是重複的。這大概是最常見、也最惱人的文字雜務之一。以下教你各種狀況怎麼清,以及背後到底發生了什麼。
一段真的從 PDF 複製出來的文字,清理後。 我們給文字清理工具這一團典型的亂 ——
This sentence was/copied out of a/PDF./ 兩個空行 /It has broken lines.—— 並勾選「收合空白」與「刪除空行」。結果保留了行的結構,但把空白修好了:This sentence was自成一行,空行消失。貼上之前值得知道:什麼都不勾的話,這個工具刻意什麼都不做。 每個勾選框就是一個轉換,所以你能精確決定發生什麼事,而不是按一個含糊的「清理」鈕、順便被改掉你沒想要改的東西。
輸入與輸出,一字不改。 一段典型的 PDF 貼上,丟進我們的文字清理工具,勾選「收合空白」與「刪除空行」:
This sentence was copied out of a PDF. It has broken lines.變成
This sentence was copied out of a PDF. It has broken lines.連續空白與空行都消失了,行的結構保留下來。而什麼都不勾時,這個工具刻意什麼都不改 —— 每個勾選框就是一個轉換,所以你不會拿到沒要求過的副作用。
移除換行(把段落接回去)
PDF 與 Email 常把一整段存成很多獨立的行,每行結尾都有一個硬換行。貼進一般編輯器後,它就維持斷開的樣子。要修好,就是把這些行重新接起來:
- 用空白接——最常用:
第一行⏎第二行變成第一行 第二行,適合一般文章段落。 - 直接相連(不留間隔)——適合被切在半途、應該連在一起的資料。
關鍵在於理解:「換行」是一個真實的字元(有時是兩個:歸位字元 + 換行字元)。移除它,其實就是把那些字元換掉。
在比對或提交任何東西之前先重新折行,有一個量得出來的理由。我們把同樣三個詞餵給文字比對工具兩次 —— 一次寫成一行、一次寫成三行,其他什麼都沒動:
−
one two three
+
one
+
two
+
three
每一行回來都被標成「有變動」。對任何以行為單位的工具來說,換行就是內容 —— 這就是為什麼從 PDF 複製出來、帶著硬換行的段落,在 diff、在重複檢查、在資料庫比對裡,永遠對不上它重新折行後的孿生兄弟。
去除重複行
清單類——Email、關鍵字、網址——最容易混進重複。去重複的做法是保留每一行第一次出現、刪掉之後所有重複,這樣能維持原本順序。如果順序不重要,清完再排 A→Z,相同項目自然會排在一起。
對以行為單位的工具來說,刪掉一行重複是成本最低的修改,而我們自己的文字比對工具可以看出為什麼。從三行裡移除一行,回傳的是
alpha
−
beta
gamma
而把它加回去,回傳的是
alpha
+
beta
gamma
各只有一行被標記,沒被動到的行維持沒有標記 —— 對照上面那個重新折行的例子,那裡是每一行都被標記。「去重複」是在 diff 之前可以安全執行的操作;「重新折行」不是,除非你對兩邊都做。
刪除空行與多餘空白
還有兩個經典:
- 空行——段落之間留下的空白列,刪掉能讓文字更緊湊。
- 多餘空白——連續兩個空白,或 Tab 變成一串空白。把每一串壓成一個空白,格式就一致了。
用真實貼上的文字量過。我們把這一段丟進文字清理工具,勾選「收合空白」與「刪除空行」:
This sentence was
copied out of a
PDF.
It has broken lines.
它回傳:
This sentence was
copied out of a
PDF.
It has broken lines.
連續空白與空行都不見了,而行的結構保留下來 —— 而且什麼都不勾時它刻意原樣回傳,因為每個勾選框就是一個轉換。
一次全部搞定,還很隱私
不必手動一個個尋找取代,文字清理器會依合理順序套用——逐行去頭尾空白、合併空白、刪空行、去重複、排序,最後才依你的選擇合併換行——結果即時顯示。全程在你的瀏覽器本機執行,再敏感的文字也不會上傳。
這也是為什麼那些操作是分開的、而不是一個「清理」鈕:你可以刪空行而不收合空白,也可以收合空白而不動行數。一個按鈕就得去猜你要哪一種。
CR、LF 與 CRLF:三種「換行」
那個看不見的換行,並不總是同一個字元。它有三種,分別來自不同作業系統:
- LF(
\n,換行 line feed)——Linux、macOS、Unix 使用。 - CRLF(
\r\n,歸位 + 換行)——Windows 的慣例。 - CR(
\r,單獨使用)——OS X 之前的老 Mac OS,今天已很少見。
多數時候你不會察覺。但當文字在不同系統之間流動,行尾不一致就會出真問題:diff 裡看起來「每一行都改了」、shell 腳本跑出莫名其妙的錯,或整段變成一長行,有時還冒出可見的 ^M。把行尾統一,這些問題一次解決。
同一個機制也解釋了 CR 與 LF。以行為單位的工具比對的是每一行結尾的位元組,所以一個檔案只要換了換行風格,看起來就像整份重寫過 —— 即使看得見的字元一個都沒移動,就跟上面那個「只重新折行就每一行都被標記」一模一樣:
−
one two three
+
one
+
two
+
three
這也是為什麼「統一換行符」要跟任何真正的修改分成兩個 commit,而審查的人會這樣要求你。
軟換行不是換行
一個常見混淆:編輯器為了塞進視窗把長行折起來,那是軟換行——純粹是視覺效果,背後沒有字元。硬換行才是你能刪掉的真實字元。移除換行只會動到硬換行;軟換行會自己重新折行,根本沒有東西可刪。
什麼時候會遇到
- 整理清單——Email、關鍵字、網址——要匯入時每列剛好一筆。
- 把一段文字貼進會把它拆碎的表單或聊天視窗。
- 重複使用前,先清乾淨 Email 簽名檔或貼來的引文。
行尾不一致也是為什麼兩份幾乎相同的檔案並排卻看起來天差地遠——如何比對兩段文字教你看穿雜訊、只盯真正的改動。