
我有一個大型(2500 萬行)資料文件,它是用垂直線 ( |
) 分隔的。資料供應商提供文件,我執行自動化作業將文件載入到 Redshift 資料庫,然後處理資料。
以下是資料範例:
123|110092|ACCT|"HC Account"|"Account1||||||||||||"Mary"|||"|"||||132|"STE|504"|1253|Unspecified Account|||N||ACTV|Active||||04/30/2013|12/31/2099|||||||||||||
到目前為止,我看到了三組現場數據:
- 文字欄位用雙引號 (
"
) 括起來,例如:"HC Account"
、"Mary"
和"|"
。這是正確的,載入資料時應不帶引號。 - 某些值將包含管道分隔符號。例如:
"STE|504"
。在這種情況下,該欄位必須用雙引號引起來。如果不是,則屬於以下第三類。 - 有時只提供起始報價而沒有結束報價。例如:
"Account1
。
TL;DR:任何以 開頭的欄位都
|"
必須以 結尾"|
。如果沒有,並且|"
遇到另一個雙引號,則必須轉義第一個雙引號。
因此,在 Unix/Python/其他建議中對其進行預處理後,我的資料行應該被編輯為以下內容:
123|110092|ACCT|"HC Account"|"Account1||||||||||||"Mary"|||"|"||||132|"STE|504"|1253|Unspecified Account|||N||ACTV|Active||||04/30/2013|12/31/2099|||||||||||||
我計劃編寫一個 Unix 腳本來使用 SED 修改該檔案。到目前為止我寫的正規表示式是:
(\|")(?!([a-zA-Z0-9]|\s|\||\/)*("\|))
但是,這無法正確匹配字串。
這是我正在測試的地方的連結:https://regexr.com/3toib
我希望保持程式碼的輕量級,因為平均檔案大小為 3-5 GB,並且通常有多個(10+)這樣的檔案。
PS Redshift 是一項使用 Postgre SQL 引擎的 AWS 資料庫服務,能夠從正確引用的欄位中刪除引號,並轉義帶有\
.
另外,考慮到程式碼重量輕,我願意用 Python/任何其他腳本語言來完成此操作。
答案1
您給出的數據規範存在一個巨大的問題。如果"|"
是一個有效的字串,或者更準確地說,允許帶引號的字串以垂直線開頭,則如果缺少結尾引號的字串(例如"Account1
)具有以豎線開頭的第一個帶引號字段(例如)"|Mary"
,則沒有辦法確定在所有情況下如果"|
是 的結束報價|"Account1||||||||||||"|
或開始報價|"|Mary"|
。
例如,使用縮短的(為了可讀性)稍微修改過的資料版本,其中從第二個開始的所有帶引號的字串都以管道開頭並且缺少結尾引號
123|110092|ACCT|"HC Account"|"Account1||||||||||||"|Mary|||"|||||132|"|STE|504|1253
可以看出,這將被錯誤地解釋為
123
110092
ACCT
"HC Account"
"Account1||||||||||||"
Mary
"|||||132|"
STE
504
1253
請注意,無論使用正規表示式、Python 或任何其他語言,這都是一個問題。一般情況問題能可以“解決”,但它會很複雜,並且需要了解每行存在多少個欄位以及這些欄位的資料結構。 (而且可能總是會有一些邊緣情況沒有被滿足。)
話雖如此,正規表示式解決方案至少可以偵測到最多左雙引號缺少右引號的情況需要多遍方法,因為正規表示式需要捕捉從每行開頭到第一個未處理的不匹配左引號的所有文字。 (否則,正如您的正規表示式所示,即使在最簡單的情況下也會發現誤報。)
所需的傳遞次數是整個文件中任何行的僅左引號欄位的最大數量加一。終止每個文件的處理需要檢測正規表示式何時不再對文件進行進一步修改。
這是適用於大多數情況的最簡單的正規表示式:
Capturing Group 1 Capturing Group 2
(All previous valid fields) (Unclosed opening quote)
__________________________|_________________________ |
| || |
^((?:(?:(?!")[^|\r\n]*|"[^"\r\n]*"(?=$|\|))(?:$|\|))*+)(")
|____________| |_________________| |______|
| | |
Unquoted field OR Quoted field EOL or hypen delimiter
將其與此替換字串一起使用:
$1\\$2