溫柔的讀者,
我在一個目錄中有一年的供應商 csv 檔案。我的任務是將它們作為“歷史負載”加載到 SQL Server 資料庫中。檔案格式錯誤,當我們與供應商合作重新發送 365 個新的、結構正確的檔案時,我的任務是嘗試使用我們擁有的檔案。
我只能使用 C#(作為 SSIS 中的腳本任務)或 Powershell。
每個檔案都沒有標頭,但架構是已知的并內置于 SSIS 包連接中。
每個檔案大約有 35k 行,每個檔案大約有幾十個格式錯誤的行。
每個正確形成的行由 122 列、121 個逗號組成。
行不是文本限定的。
示例:(清除 PII 的資料)
555222,555222333444,1,HN71232,1/19/2018 8:58:07 AM,3437,27.50,HECTOR EVERYMAN,25-Foot Garden Hose - ,1/03/2018 10:17:24 AM,,1835,,,,,online,,MERCH,1,MERCH,MI,,,,,,,,,,,,,,,,,,,,6611060033556677,2526677,,,,,,,,,,,,,,EVERYMAN,,,,,,,,,,,,,,,,,,,,,,,,,,,,,VWDEB,,,,,,,555666NA118855,2/22/2018 12:00:00 AM,,,,,,,,,,,,,,,,,,,,,2121,,,1/29/2018 9:50:56 AM,0,,,[CRLF]
555222,555222444888,1,CASUAL50,1/09/2018 12:00:00 PM,7000,50.00,JANE SMITH,$50 Casual Gift Card,1/19/2018 8:09:15 AM,1/29/2018 8:19:25 AM,1856,,,,,online,,FREE,1,CERT,GC,,,,,,,6611060033553311[CRLF]
,6611060033553311[CRLF]
,,,,,,,,,25,,,6611060033556677,2556677,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,CASUAL25,VWDEB,,,,,,,555222NA118065,1/22/2018 12:00:00 AM,,,,,,,,,,,,,,,,,,,,,,,,1/19/2018 12:00:15 PM,0,,,[CRLF]
555222,555222777666,1,CASHCS,1/12/2018 10:31:43 AM,2500,25.00,BOB SMITH,BIG BANK Rewards Cash Back Credit [...6S66],,,1821,,,,,online,,CHECK,1,CHECK,CK,,,,,,,,,,,,,,,,,,,,555222166446,5556677,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,VWDEB,,,1/23/2018 10:30:21 AM,,,,555666NA118844,1/22/2018 12:00:00 AM,,,,,,,,,,,,,,,,,,,,,,,,1/22/2018 10:31:26 AM,0,,,[CRLF]
Powershell Get-Content(我認為...)將檔案讀入一個陣列,其中每一行都由 CRLF 標識為終止符。這意味著(再次,我認為)格式錯誤的行將被視為陣列的一個元素,而不管它擁有多少“列”。
C# Streamreader 也使用 CRLF 作為標記,但 streamreader 物件也有一些可用的方法,如 Peek 和 Read,可能很有用。
請,哦,明智的人,將我指向阻力最小的方向。使用 Powershell 作為腳本來處理格式錯誤的 csv 檔案,以便洗掉不是 EOL 的 CRLF。
謝謝你。
uj5u.com熱心網友回復:
基于@vonPryz設計,但在(本機1)PowerShell 中:
$Delimiters = 121
Get-Content .\OldFile.csv |ForEach-Object { $Line = '' } {
if ($Line) { $Line = ',' $_ } else { $Line = $_ }
$TotalMatches = ($Line |Select-String ',' -AllMatches).Matches.Count
if ($TotalMatches -ge $Delimiters ) {
$Line
$Line = ''
}
} |Set-Content .\NewFile.Csv
1)我想通過避免 =和使用 dot .net 方法以及文本流媒體可能會提高性能
uj5u.com熱心網友回復:
老實說,最好的辦法是從供應商那里獲得好的資料。試圖解決一團糟只會在以后引起問題。垃圾進垃圾出。既然是你在資料庫里寫了垃圾資料,恭喜你,現在資料庫資料質量差是你的錯。請先與您的經理和利益相關者交談,以便您達成書面協議,表明您沒有破壞資料并且從一開始就破壞了資料。我經常在 ETL 處理中看到這樣的問題。
一個快速而骯臟的偽代碼,沒有錯誤處理、邊緣情況處理、子字串索引假設、性能保證等等,
while(dataInFile)
line = readline()
:parseLine
commasInLine = countCommas(line)
if commasInLine == rightAmount
addLineInOKBuffer(line)
else
commasNeeded = rightAmount - commasInLine
if commasNeeded < 0
# too many commas, two lines are combined
lastCommaLocation = getLastCommaIndex(line, commasNeeded)
addLineInOKBuffer(line.substring(0, lastCommaLocation)
line = line.substring(lastCommaLocation, line.end)
goto :parseline
else
# too few lines, need to read next line too
line = line.removeCrLf() readline()
goto :parseline
這個想法是首先你尋找一行并計算有多少個逗號。如果計數與預期相符,則該行不會被破壞。將其存盤在包含良好資料的緩沖區中。
如果逗號太多,則該行至少包含兩個不同的元素。然后找到第一個元素結束的位置的索引,提取出來存放在好資料緩沖區中。然后洗掉已處理的部分行并重新開始。
如果逗號太少,則該行會被換行符分割。從檔案中讀取下一行,將其與當前行連接起來,并從計算行數開始再次決議。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/328038.html
