去年底我替一位同事接手了一次商品編碼遷移。手頭有一份後台匯出的清單,120 行,一行一個編碼,形如 SKU-10086。他要把它拼進一條 SQL 的 IN 條件,要求每行變成「引號包住、後面帶逗號」。我心想這點活手動改就是了,結果改到第四十幾行漏了一個逗號,第六十幾行的行尾多出一個空格,整段貼過去直接報錯,回頭找問題花的時間比預估多了好幾倍。從那以後,凡是「每一行都要做同一件事」的活,我不再逐行敲。
手動逐行改,錯誤全藏在行尾和最後一行
逐行加引號難的不是打字,而是每行都得一模一樣。人眼看著兩個字串完全相同,實際一個結尾帶空格一個不帶,SQL 裡就是兩條不同的值。我數過那次的三類錯:漏逗號、多空格,還有最隱蔽的最後一行。前 119 行都該是 "SKU-10086",,最後那行不該有尾隨逗號,多數資料庫遇到 IN ('a','b',) 會當語法錯誤。規則本身就是「其他行一樣、最後一行不一樣」,這恰恰最不適合人來做。
我也試過尋找取代。把每行取代成 "$1", 這種帶回引的寫法本來是對的,但那份檔案是在 Windows 下匯出的,行尾是回車加換行兩個字元,我的模式只匹配了換行,取代完每行末尾拖著一個看不見的回車,貼進查詢編輯器照樣報錯。
前後綴:把每行變成程式碼裡的字面量
這份清單要的目標形態,本質就是給每行左邊加一個字元、右邊加兩個字元。用 文字行操作 的前後綴模式,前綴填一個 ASCII 雙引號 ",後綴填 ",(也就是先引號再逗號),120 行一次出來就是 "SKU-10086", 的形態,可以直接貼進括號裡。要點在於後綴裡那兩個字元的順序:寫成 "," 出來就是「逗號在前」,貼上去必然語法錯。
換語言時只是換引號種類。JavaScript 陣列裡我更習慣單引號,前綴填 '、後綴填 ',;值本身可能含單引號時,先決定好用哪種引號包,別等拼完再手工找衝突。
"SKU-10086",
"SKU-10087",
"SKU-20011",
序號不是打上就行,起始值和步長要提前定
另一件常事是編號。給目錄、題目加 1. 2. 看著沒什麼可說,但兩個決定影響後面很多事。一是起始值:我有一批清單接續上月,上月編到 100,這一批必須從 101 開始,而不是補 100 行空內容。二是步長:按 5 或 10 遞增是為了以後往中間插條目時不必重排;按 1 遞增最自然,可一旦要在第 3 條和第 4 條之間加東西,就出現 3.5 這種編號。
補零這件事我是吃過虧才重視的。純字串排序下 10. 會排在 2. 前面,因為比的是第一個字元。清單條目接近或超過 10 項,我就用 01. 02. 這種補齊形式,讓編號長度一致,之後交給排序或者放進檔名都不會錯位。
刪字元和擷取:先數準位置,再看行長是否一致
加前綴的反向操作我也常用。那批編碼裡的 SKU- 是固定 4 個字元,需要純數字時我選「刪除每行左側 4 個字元」,只想留前幾位就用擷取。用這類模式前我會先掃一遍最短行:刪除和擷取按位置走,本來比 n 個字元短的行不可能給你完整目標,只會剩下殘段甚至空行。所以我習慣再對照一次行數,行數和輸入不等就說明有行被「刪空」了。
下面這張表是我當時把幾種目標形態倒推出來的填法,字元都是照抄可用的,讀者可以直接對著試。
| 目標形態 | 前綴填 | 後綴填 | 序號設定 | 輸入範例 | 輸出範例 |
|---|---|---|---|---|---|
| SQL 的 IN 片段 | " | ", | 關閉 | SKU-10086 | "SKU-10086", |
| JS 陣列元素(單引號) | ' | ', | 關閉 | SKU-10086 | 'SKU-10086', |
| 帶編號的清單 | 留空 | 留空 | 起始 1,步長 1,補零兩位 | 蘋果 | 01. 蘋果 |
| 接續上月的編號 | 留空 | 留空 | 起始 101,步長 5 | 蘋果 | 101. 蘋果 |
| 去掉固定前綴 | — | — | 刪除左側 4 個字元 | SKU-10086 | 10086 |
表格公式我也用,但不是所有場合都划算
如果資料本來就在表格裡、還要順帶算別的欄,公式更順手。在 Excel 裡給 A1 加引號和逗號,我會寫成 =CHAR(34)&A1&CHAR(34)&",",其中 34 是雙引號的十進位碼點,這樣比連寫四個雙引號去轉義更好讀,也不容易數錯。往下填滿 120 行、複製、再「只貼上文字」覆蓋回來,動作也不慢。
但我現在只在兩種情況下選公式:欄本身要參與計算,或者檔案已經開在表格裡。反過來,純文字清單我不願走這條路——貼回文字要多一次「貼上為值」,而表格還會自作聰明地改內容:12 位純數字串顯示成科學記號,超過 15 位精度直接丟掉,形如 2024120310001 這種帶日期意味的編碼還可能被辨識成數值。需要先把清單變成規範列表時,我一般先用文字排序去重處理掉重複項再做前後綴,順序反過來的話重複行會變成重複的帶引號行,看起來像是資料本身有問題。
遇到「改某一行的中間」這類需求,我會換工具
前後綴、序號、按位置刪除和擷取,覆蓋的是「每行動作相同」的場景。一旦每行的插入點不同——比如把 name=張三名 變成 name='張三名'——就不是行級批次動作了,得用帶模式的取代,這時我會去用批次尋找取代,把值捕獲出來再加引號。分清一件事屬於「整行的位置規律」還是「行內的內容規律」,比記住哪個工具能幹什麼更有用。
幾個我當時想過的問題
Q:一行裡內容長短不一,加前綴會不會加錯位置?
前後綴和序號跟行長無關,它們是接在行首、行尾的,多長都一樣處理,所以加引號加逗號這類動作我從不擔心行不齊。真正跟長度有關的是「刪除左側 n 個字元」和「擷取前 n 個」,這兩種要提前確認最短行也夠長。
Q:序號補零除了好看還有別的用處嗎?
有用處,主要在排序和檔名上。字元長度一致,字串排序才會和數值排序一致;放進檔名時 01.jpg 到 12.jpg 能按序排,而 1.jpg、10.jpg、2.jpg 就會亂。
Q:幾百行的清單一次處理完,結果怎麼核對?
我看兩個數:輸出行數與輸入行數是否相等,末尾那一兩行是否符合預期。行數不等說明有行被刪空或有空行混進來;末尾不對通常是逗號位置或者編號到哪。這兩步比我上次那種「改完直接貼」可靠得多。
Q:原檔案裡的空行會被一起加引號嗎?
會被當成一行處理,出來就是一對引號加逗號,屬於憑空多出的項。所以我先清理空行再做行級操作,清單有重複的話也在那一步順手去掉。
Q:最後那行的尾隨逗號怎麼處理最穩?
我傾向於「不管它」:統一按帶逗號產生,貼進括號後再刪掉最後一個。真正危險的是靠人記住「最後一行不一樣」,一百行裡漏一次就得回頭查半天。
回到開頭那份 120 行的清單。現在我的判斷依據很簡單:動作對每行都相同、且規律能用位置描述(行首、行尾、序號、第幾個字元),交給行級批次處理;動作取決於行內內容,用尋找取代;資料本身還要參與計算、又已經躺在表格裡,才用公式。那次的教訓不是「手動太慢」——慢只是表象,真正浪費時間的是「最後一行例外」這類規則,機器不會漏,人一定會。