在 Notepad++ 中為每一行加上引號的標準做法,是啟用正規表示式後,做兩次 Find and Replace:先把行首錨點 ^ 換成 ",再把行尾錨點 $ 換成 ",這樣就能包住整個檔案。這種做法適用於任何內容不含內嵌引號字元的純清單,但一旦輸出結果要放進 JSON、SQL 或 CSV 中,就不夠用了,因為沒有任何內嵌引號會被跳脫。RFC 8259 規定,JSON 字串字面值裡的雙引號必須變成 \";PostgreSQL 與 SQLite 所記載的 ISO SQL 規則規定,字串字面值裡的單引號必須加倍成 '';RFC 4180 則規定,含有雙引號的 CSV 欄位,必須以加倍的方式編碼。這三條規則彼此不同,而這種差異,正是這些預設值所要編碼的內容,每一種都緊扣著它所引用的那份標準。

Notepad++ 中的正規表示式做法(以及它究竟做了什麼)
在 Notepad++ 中,按 Ctrl+H 開啟 Find and Replace。在底部的 Search Mode 選項中,選擇「Regular expression」。保持「Wrap around」勾選,讓這項操作能繼續執行到檔案結尾之後。這裡重要的兩個錨點是 ^ 與 $,兩者都是零寬度的:它們比對的是一行的起點與終點這個位置,並不會消耗任何字元。
第一輪,在 Find what 中輸入 ^,在 Replace with 中輸入 ",然後點擊 Replace All。這會在檔案中每一行的開頭,包括空白行,都加上一個雙引號。第二輪,在 Find what 中輸入 $,在 Replace with 中輸入 ",再點擊一次 Replace All。現在每一行的兩端都有一個雙引號。Notepad++ 使用的是 Boost 正規表示式,預設會把 ^ 與 $ 當作行錨點處理,所以一行以 Windows 的 CRLF 結尾時,$ 仍然會比對到行的邊界,而不是比對到歸位字元本身。
如果你只想包住非空白行,正規表示式就要稍微長一點。搜尋 ^.+?$,Replace with 填 "$0"。這個 $0 標記代表整個比對結果,所以引擎會把比對到的內容,插進兩個字面上的引號字元之間。空白行沒有東西可比對,會自動被跳過。同樣的做法改成單引號,就變成 '$0',適合用於 SQL 的 IN 清單,不過跳脫仍然得自己手動處理。
正規表示式做法在哪裡會失靈
正規表示式的技巧能乾淨俐落地完成包住這個動作。它處理不了的,是跳脫,而跳脫正是決定這份輸出能不能直接貼上使用的關鍵。想像一份含有帶撇號姓名的清單:O'Brien、D'Angelo、Mc'Coy。經過 Notepad++ 的正規表示式包裝之後,每一行都會變成"O'Brien"。如果把這個結果貼進 PostgreSQL 查詢,當作 IN 子句的一部分,資料庫會拒絕它:SQL 字串字面值是用加倍的單引號當作跳脫符號,而不是反斜線。根據PostgreSQL documentation on lexical structure,'O'Brien' 是語法錯誤;'O''Brien' 才是正確的字面值。
同樣的輸入、同樣的包裝方式,對 JSON 陣列來說,會產生不同的輸出結果。一行含有say "hi"的內容,經過正規表示式包裝後,會變成"say "hi""。RFC 8259 規定,內嵌的雙引號必須以反斜線跳脫,所以正確的 JSON 字串字面值應該是"say \"hi\""。把沒有跳脫過的版本貼進 JSON.parse,剖析器會在第一個內嵌引號處失敗。
CSV 檔案遵循的,則是第三種規則。依照RFC 4180的規定,含有逗號、雙引號,或換行符的欄位,必須用雙引號括起來,而任何內嵌的雙引號,都要以加倍的方式跳脫。所以欄位say "hi"會變成"say ""hi"""。正規表示式做法給你的,會是"say "hi"",這是格式錯誤的 CSV:欄位的結尾標記是錯的,剖析器會把它讀成三個欄位,而不是一個。
針對 JSON、SQL 與 CSV 的更快做法
這是每個開發者都熟悉的小麻煩:同事在聊天訊息裡丟來五十個 ID,而你需要把它們變成一個 JSON 陣列、一個 SQL IN 子句,或一行加了引號的 CSV。在 Notepad++ 裡手動完成,代表每種格式都要各自做三輪 Find and Replace,還得手動跳脫任何內嵌的引號字元,再加上外面的括號或圓括號。Add Quotes to Each Line這款工具,把整個流程壓縮成每種格式只要按一次,而且該格式對應的跳脫規則已經套用好了。
這些預設值編碼了上面提到的三個標準,再加上第四種、給 JavaScript 樣板字面值用的風格。每一種預設值,都緊扣著它所依循的標準,所以 JSON 輸出對 RFC 8259 來說是有效的,SQL 輸出對 ISO SQL 字串字面值規則來說是有效的,CSV 輸出對 RFC 4180 來說也是有效的。同一行輸入,在每一種預設值下,會得出不同、但各自正確的輸出。
如何使用這款工具
- 把你的清單貼進輸入欄位,每行一個項目。
- 點擊你需要的格式所對應的預設值:JSON array、SQL IN clause、CSV row,或 plain quotes。
- 如有需要,微調引號樣式、分隔符號,或跳脫方式,再複製這份可直接貼上使用的結果。
這些預設值所執行的每一個步驟,都有獨立的控制項可用。你可以選擇引號字元,甚至完全不用引號,設定分隔符號、決定最後一行後面要不要加上分隔符號、切換跳脫功能的開關、略過空白行,並修剪每一行前後的空白。結果會回報處理了多少行,這有助於確認輸入內容是依你預期的方式讀取的。
這款工具能在單一線性流程中,處理多達一百萬個字元的輸入,所以即使是非常長的清單,也能瞬間回傳結果。一切都在瀏覽器中執行,這一點很重要,因為開發者餵給這類工具的清單,往往正是他們不想上傳到隨便一個網站的那種 ID 或姓名資料。沒有任何資料會離開這個分頁。
依格式而定的跳脫規則
同一行輸入,會依目標格式的不同,產生不同、但各自正確的輸出。這些差異並不是風格上的問題;而是由每種格式所引用的標準明確定義的。下表比較了這款工具提供的四種預設值。
| 格式 | 引號字元 | 內嵌引號規則 | 標準 |
|---|---|---|---|
| JSON 字串 | 雙引號 " | 反斜線跳脫:\" | RFC 8259 |
| SQL 字串字面值 | 單引號 ' | 把引號加倍:'' | ISO SQL、PostgreSQL、SQLite |
| CSV 欄位 | 雙引號 " | 把引號加倍:"" | RFC 4180 |
| JS 樣板字面值 | 反引號 ` | 反斜線跳脫:\` | ECMAScript |
JSON 陣列會把每個項目都用雙引號包起來,用反斜線跳脫內嵌的雙引號,並用方括號把整份清單包起來。SQL 的 IN 清單會把每個項目都用單引號包起來,把任何內嵌的單引號加倍,並用圓括號把整份清單包起來。CSV 的一列,會把每個項目都用雙引號包起來,把任何內嵌的雙引號加倍,用逗號把它們串在同一行,並在結尾不加上多餘的逗號。反引號預設值則適用於 JavaScript 樣板字面值,會跳脫反引號與 ${ 這個插值開頭符號,避免貼進去的內容意外注入一段運算式。
Notepad++ 對比瀏覽器工具
當清單很短、內容單純,而你只需要用雙引號快速包一下時,就用 Notepad++ 的正規表示式做法。這兩輪 Find and Replace 只需要幾秒鐘,而且檔案會一直開在你的編輯器裡,方便後續編輯。這也是一個很有用的學習練習:這種雙錨點做法,可以推廣到任何包裝模式,包括用自訂分隔符號括住每一行。
當這份清單需要放進會把輸出當成語法來解讀的程式碼或資料中時,就用瀏覽器工具。JSON、SQL 與 CSV 都各自有跳脫規則,一旦弄錯了,代價可能是剖析時的語法錯誤、執行期的例外狀況,或 CSV 匯入時悄悄發生的資料損毀。一款會依格式套用正確規則的工具,能消除這種風險,並讓你只按一下,就得到可直接貼上使用的輸出。
如果你想用不靠正規表示式的 Notepad++ 流程,改用 Column Mode 與 Edit 選單,而不是 Find and Replace,可以參考adding quotes to each line in Notepad++ without regex這篇姊妹指南。如果你的包裝不需要任何規則,只是想在每一行兩側加上固定的文字,Add a Prefix and Suffix to Lines這款工具能處理任意文字,完全不涉及跳脫邏輯。
有一項行為值得任何加引號工具的使用者留意:對它自己的輸出結果再跑一次,會再包一層,因為一款加引號工具,沒辦法分辨輸入內容裡既有的引號,究竟是原本的內容,還是先前包裝留下來的。如果你需要用不同設定重新包裝,請從最原始的清單開始。
如果你正在比較各種做法,Column to Comma Separated List: A Quote-Safe Browser Tool有詳細的說明。