【问题标题】:Using Powershell to remove illegal CRLF from csv row使用 Powershell 从 csv 行中删除非法 CRLF
【发布时间】:2021-10-20 03:55:05
【问题描述】:

温柔的读者,

我在一个目录中有一年的供应商 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。

谢谢。

【问题讨论】:

  • 无意不尊重。这个解决方案将是一团糟。你能在没有额外 {CRLF} 的情况下输出吗?这似乎是您从命令行或程序复制和粘贴信息,因此额外的 [CRLF]。假设无法做到这一点。我们的问题是您的最长行是 353。请确保它的自动换行长度正确。如果有一个非常容易理解的模式,我们才有机会解析这个。
  • 谢谢亚伦。我从 Notepad++ 中复制了这个并添加了 CRLF 以获得视觉提示。同意,这是一团糟。
  • 您能否更具体地说明某些行的“格式错误”?
  • 是否会发生额外的 crlf 驻留在例如地址字段中并用于分隔应该在一个数据库列中一起出现的多个地址行?如果一行有两条地址线而另一行有三条,这将导致大量问题。在这种情况下,它几乎是 GIGO。
  • 是否可以安全地假设 (i) 所有行都以 crlf 正确终止 (ii) 字段数据中没有逗号?如果是这样,您可以一次手动读取一个文本行,将它们以逗号分隔并将它们累积到一个数组中,直到您拥有一行的值(121?),然后处理该行,并再次开始累积值。跨度>

标签: powershell ssis etl


【解决方案1】:

基于@vonPryz 设计,但在(Native¹)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 方法和文本流媒体来提高

【讨论】:

  • 谢谢@iRon。这正是我正在寻找的,它按预期工作。来自索诺兰沙漠的爱。
【解决方案2】:

老实说,您最好的选择是从供应商那里获得良好的数据。试图解决混乱只会在以后引起问题。垃圾进垃圾出。既然是你在数据库里写了垃圾数据,那么恭喜你,现在数据库数据质量差是你的错。请先与您的经理和利益相关者交谈,以便您以书面形式签署协议,说明您没有破坏数据并且一开始就被破坏了。我经常在 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

这个想法是,首先您要查找一行并计算有多少个逗号。如果计数与预期相符,则该行不会中断。将其存储在包含良好数据的缓冲区中。

如果逗号过多,则该行至少包含两个不同的元素。然后找到第一个元素结束位置的索引,将其提取并存储在良好的数据缓冲区中。然后删除该行已经处理的部分并重新开始。

如果逗号太少,则该行将由换行符分隔。从文件中读取下一行,将其与当前行连接起来,然后从计算行数重新开始解析。

【讨论】:

  • 感谢您的努力和时间,vonPryz。感谢您的回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-24
  • 1970-01-01
相关资源
最近更新 更多