我有一个源自Linux Server的csv文件,因此它们包含\ n来标记一行的结尾。现在,我通过Powershell脚本处理此文件,并将其“文本限定符”替换为其他内容(之所以这样做,是因为我正在使用SSIS将CSV提要上传到数据库,并且出于某些奇怪的原因,SSIS不支持“嵌入式文本限定符” )
执行此替换的部分脚本如下所示
gc $file.FullName |
% { if($_.indexOf("|~|") -eq -1) {$_ -replace "`"((?:`"`"|.)*?)`"(?!`")", "|~|`$1|~|" -replace "`"`"", "`""} else {$_ -replace " ", " "}} |
sc $temppath
该脚本可以正常工作,但也可以将换行符最后更改为\ r \ n,直到我意识到我的原始提要在description列中偶尔包含\ 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()?
问题在于,通过传递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文件对系统资源的限制,因此可能是一个限制.NET类。
但是,如果那仍然给您带来内存错误,或者它起作用但对系统资源造成了过多负担,则可以使用System.IO.File的AppendAllText方法在管道的每次迭代中一次将行添加到文件中。在不添加换行符的情况下追加行(Out-File -Append
将执行哪种管道操作),并"`n"
在每行上添加a :
Get-Content $file | %{
[System.IO.File]::AppendAllText($temppath, $(
(if ($_.indexOf("|~|") -eq -1) {
$_ -replace "`"((?:`"`"|.)*?)`"(?!`")", "|~|`$1|~|" -replace "`"`"", "`""
} else {
$_ -replace " ", " "
}) + "`n"
))
}
这样会比较慢,但是大大减少了内存消耗。
注意,顺便说一句,这gc $file.FullName
是多余的,因为FileInfo对象作为其FullName属性隐式转换为字符串,所以gc $file
就足够了。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句