保留换行和回车Powershell

时间:2014-07-22 09:12:02

标签: powershell csv ssis

我有一个源自Linux Server的csv文件,所以它们包含 \ n 来标记一行的结尾。现在我通过powershell脚本处理此文件并替换它的文本限定符'其他事情(我这样做是因为我使用SSIS将csv源上传到数据库,并且由于一些奇怪的原因,SSIS不支持嵌入式文本限定符')

执行此替换的脚本的一部分看起来像这样

gc $file.FullName |
    % { if($_.indexOf("|~|") -eq -1) {$_ -replace "`"((?:`"`"|.)*?)`"(?!`")", "|~|`$1|~|" -replace "`"`"", "`""} else {$_ -replace " ", " "}} |
    sc  $temppath

这个脚本运行正常,但也会将最后的换行符更改为 \ r \ n 我明白应该不会遇到那么大的问题,直到我意识到我的原始Feed还偶尔包含描述栏中的 \ r 也被" \ r \ n" 取代现在SSIS包无法识别' csv行结束。

我搜索并发现这是由于Get-Content逐行工作所以我将命令更改为以下内容。

[System.IO.File]::ReadAllText($file.FullName) |
            % { if($_.indexOf("|~|") -eq -1) {$_ -replace "`"((?:`"`"|.)*?)`"(?!`")", "|~|`$1|~|" -replace "`"`"", "`""} else {$_ -replace " ", " "}} |
            sc  $temppath

这似乎解决了我的问题,但现在我被困在*" OutOfMemoryException"因为一些csv文件很大(约400-500 MB)*

任何建议我可以做什么?或许可以替代适用于大文件的ReadAllText()吗?

1 个答案:

答案 0 :(得分:2)

问题在于,通过管道 Get-Content 的输出,您将文件转换为单独的行,然后 Set-Content 组合这些行进入一个新文件。由于CR / LF是在Windows中分隔行的方式,因此PowerShell cmdlet用于将行组合到文件中(您使用 Out-File 获得相同的行为,这并不奇怪)。但你已经知道了。现在解决方案是什么?

一种方法是使用 -join 运算符将所有行连接成一个字符串,该字符串由LF字符分隔的行组成,并将该字符串传递给 Set-Content

(Get-Content $file | %{
  if ($_.indexOf("|~|") -eq -1) {
     $_ -replace "`"((?:`"`"|.)*?)`"(?!`")", "|~|`$1|~|" -replace "`"`"", "`""
  } else {
    $_ -replace " ", " "
  }
}) -join "`n" | Set-Content  $temppath

虽然这会将所有行连接成需要存储在内存中的单个字符串,但我强烈怀疑这对你来说会更好,因为OutOfMemoryException不太可能是500MB文件的系统资源限制,所以它& #39;可能是.NET类的限制。

但是,如果仍然存在内存错误,或者它仍然有效,但对系统资源过于沉重,则可以使用 System.IO在管道的每次迭代中一次添加一行到文件中.File AppendAllText 方法可以在不添加换行符的情况下附加行(Out-File -Append 的管道会执行),并添加每个"`n"

Get-Content $file | %{
  [System.IO.File]::AppendAllText($temppath, $(
    (if ($_.indexOf("|~|") -eq -1) {
       $_ -replace "`"((?:`"`"|.)*?)`"(?!`")", "|~|`$1|~|" -replace "`"`"", "`""
    } else {
      $_ -replace " ", " "
    }) + "`n"
  ))
}

速度会慢一些,但内存密集程度却会大幅降低。

请注意,BTW,gc $file.FullName是多余的,因为FileInfo对象被隐式转换为字符串作为 FullName 属性,因此gc $file就足够了。