Linux awk用逗號切CSV欄位錯位:引號裡的逗號怎麼辦

把商品CSV交給awk -F,,第二欄本來應是Coffee, milk,結果只剩Coffee,後面的狀態也往後移。問題不是文字編碼,而是你用每個逗號切欄位,沒有把雙引號內的逗號當成內容。普通分隔文字與帶引用規則的CSV,不是同一種解析任務。

先用一筆含逗號的名稱重現,再改用真正理解CSV的解析器。不要先把逗號刪掉,或者把所有雙引號移除;那會改變商品名稱,也可能讓原本合法的資料變成另一份意思不同的檔案。

一個CSV欄位可以包含逗號

id,title,status
1,"Coffee, milk",ready
2,"She said ""Hi""",draft

這三行示範header與兩筆資料,每筆都應有三欄。第一個標題包含逗號,所以用雙引號括起來;第二個標題內的雙引號,用兩個連續雙引號表示。這不是多了一欄,而是CSV引用的內容表示方式。

RFC 4180描述常見的逗號、雙引號與換行規則,是這裡比較的格式依據。它不是宣稱所有叫CSV的檔案都使用相同方言;有些匯出會用分號、不同引用方式或額外編碼標記,仍要查真正產生檔案的工具約定。

awk -F只設定欄位分隔,不自動理解引用

把範例保存為example.csv,再查看欄數與第二欄。這個命令只讀取檔案,不改寫來源。

awk -F, '{ print NF, $2 }' example.csv

在隔離Ubuntu 24.04、mawk 1.3.4的實跑,header顯示三欄;Coffee那筆卻顯示四欄,第二欄只剩開頭引號與Coffee。She said那筆雖然仍是三欄,第二欄保留CSV用來轉義的引號,不是已還原的標題文字。

欄數偶爾對了,也不表示已正確解析。第二筆沒有逗號,所以簡單切分看不出錯位,卻仍沒有按引用規則還原內容。這就是只拿一筆沒有特殊字元的資料測試,很容易誤以為awk -F,已支援CSV的原因。

awk本身是有用的文字處理工具,這個例子的問題在你選的切分方式。當輸入確實是一種不允許引用或內含分隔符的簡單格式,固定分隔可以符合約定;如果檔案使用CSV引用,就不能用同一個假設解讀。

用Python csv.reader還原欄位

Python標準函式庫提供csv.reader。讀文字檔時設定newline空字串,讓CSV模組按自己的規則處理換行;編碼則依真實檔案約定指定。以下限定為UTF-8範例,不對未知來源自動猜編碼。

import csv

with open('example.csv', newline='', encoding='utf-8') as f:
    for row in csv.reader(f):
        print(len(row), row[1])

隔離Linux Python 3.12.14實跑,三行都得到三欄;兩筆標題分別還原為Coffee, milk與She said “Hi”。引號中的逗號仍在同一個標題,轉義雙引號也還原成一個字元。這份對照只使用合成資料,沒有操作正式商品或客戶名單。

你可以先將讀出的每一列印出來,比對欄數和內容,再逐步加入真正需要的匯入動作。讀取成功不代表欄位符合業務條件,id是否有效、狀態是否允許、重複資料如何處理,都應在解析之後另做驗證。

不要把一行必然當成一筆資料

帶引用的CSV還可能在同一個欄位內包含換行。若先用shell逐行讀取,再對每行做簡單拆分,就可能把一筆資料切成兩筆。本文的兩筆真實跑資料沒有這種換行,但格式規則允許它,解析器選擇應把這個條件考慮進來。

同理,先把原檔案丟給按行計算的工具,得到的行數不一定等於CSV資料筆數。需要報匯入幾筆時,應按解析後的記錄計算,並明確說明header是否計入。不能拿檔案多了幾行就判定多了幾件商品。

如果來源約定禁止欄位內換行,仍可以在解析後拒絕那種內容;這是一個檢查規則,而不是略過CSV解析的理由。先還原資料,再按契約檢查,較容易清楚指出是哪個欄位不符合需求。

GNU awk的CSV模式有版本條件

GNU awk從5.3版加入CSV欄位處理與–csv選項。這是特定實作的功能,不能看到系統有awk命令就直接使用。本文Ubuntu的awk是mawk,不是GNU awk,因此沒有拿–csv當成這台實跑環境的修正指令。

若你的系統確實安裝支援版本的gawk,可以按它的CSV模式文件評估。但不要把-F,和–csv混為同一功能,也不要只改成一段更長的正規表達式,就宣稱已處理任意引用與嵌入換行。先核版本、資料方言與實際邊界案例。

不需要為了這個小型匯入任務強制更換所有系統的awk。若環境已有符合要求的Python標準模組,使用它即可把解析責任寫清楚;若有既定的CSV匯入工具,也可沿用它,再驗證相同資料。工具選擇應符合你的部署環境與維護方式。

解析後先驗每列,再寫入

至少準備普通標題、含逗號標題、含雙引號標題,以及缺欄位的資料。前三種應還原成正確文字,缺欄位則應依契約報錯。不要在欄數不足時自動把最後一欄補成ready,否則原本壞掉的紀錄可能被當成可用商品。

如果匯出有header,可以核對欄位名稱與順序,再按名稱對應資料。某個工具把status移到第二欄時,只按$2固定位置處理就可能讀錯。這裡使用小型三欄例子,是為了看懂解析問題,不要求你把所有真實匯入都固定成這個順序。

保留原始檔案,在新的輸出或隔離資料庫驗證,避免直接覆寫來源。遇到錯誤時應能指出是編碼、引用結構、欄數還是業務值,而不是只有「匯入失敗」一行。清楚的分層能讓提供資料的人知道要改哪一部分。

如果要再輸出CSV,也用對應的writer處理引用,不要把每欄用逗號直接拼回去。讀取時成功保住標題內的逗號,輸出時卻沒有加引用,下一個工具仍會再切錯。輸入與輸出各自遵守同一份格式約定,資料才會在工具之間保持原意。

最後,把上面三行保存為回歸資料,每次更換匯出來源或解析工具後重新比對。你要確認的是欄位邊界與內容都正確,而不只是程式沒有拋錯。正式匯入的權限、交易與重複處理仍依自己的流程執行。

這份實跑把header也當一列印出,因此輸出三列、真正資料是兩筆。接到匯入程式時,應明確處理header,不要把id、title、status這三個名稱當成第一件商品。若來源沒有header,則依已約定的欄位順序讀取,不要自動跳過第一筆真資料。

參考資料

原文鏈接:https://wntheme.com/linux-awk-comma-csv-quoted-field/,轉載請註明出處。
0

評論0

顯示驗證碼
沒有帳號?註冊  忘記密碼?