获取内容的替代方案

时间:2015-09-01 16:35:15

标签: powershell out-of-memory

我目前有以下代码行。

(Get-Content 'file.txt') |
  ForEach-Object {$_ -replace '"', ''} |
  Set-Content 'file.txt'

这在测试时有效,但现在我尝试在真实数据文件(13 GB)上使用它,这个使用Get-Content的过程导致Powershell消耗大量RAM并最终消耗所有可用RAM机器。

有没有更好的方法可以在没有相同开销的情况下实现相同的结果?

似乎我的做法与最佳做法相反,但不确定还有什么比上面更清洁/更少RAM。

3 个答案:

答案 0 :(得分:6)

使用流来读取文件,然后它不会将其全部放入内存,您也可以使用流来写入输出。这应该很好,并保持内存使用率下降:

$file = New-Object System.IO.StreamReader -Arg "c:\test\file.txt"
$outstream = [System.IO.StreamWriter] "c:\test\out.txt"

while ($line = $file.ReadLine()) {
  $s = $line -replace '"', ''
  $outstream.WriteLine($s)
}
$file.close()
$outstream.close()

答案 1 :(得分:5)

您的问题不是由Get-Content引起的,而是由您在表达式中运行语句(即在括号中)引起的。像这样运行Get-Content是允许管道将数据写回同一文件的便捷方式。但是,这种方法的缺点是在将数据传递到管道之前将整个文件读入内存(否则,当Set-Content尝试将数据写回到文件时,文件仍将打开以供读取。)< / p>

要处理大型文件,必须删除括号,并将输出写入您之后重命名的临时文件。

Get-Content 'C:\path\to\file.txt' |
  ForEach-Object {$_ -replace '"', ''} |
  Set-Content 'C:\path\to\temp.txt'

Remove-Item 'C:\path\to\file.txt'
Rename-Item 'C:\path\to\temp.txt' 'file.txt'

这样做可以避免您观察到的内存耗尽。通过将读取计数增加为@mjolinor建议(在我的测试中将执行时间减少到大约40%),可以进一步加快处理速度。

为了获得更好的效果,请使用StreamReaderStreamWriter @campbell.rw建议的方法:

$reader = New-Object IO.StreamReader 'C:\path\to\file.txt'
$writer = New-Object IO.StreamWriter 'C:\path\to\temp.txt'

while ($reader.Peek() -ge 0) {
  $line = $reader.ReadLine().Replace('"', '')
  $writer.WriteLine($line)
}

$reader.Close(); $reader.Dispose()
$writer.Close(); $writer.Dispose()

Remove-Item 'C:\path\to\file.txt'
Rename-Item 'C:\path\to\temp.txt' 'file.txt'

答案 2 :(得分:1)

这应该比逐行处理更快,并且仍然可以控制内存消耗:

Get-content 'file.txt' -ReadCount 5000 |
 foreach-object {$_ -replace '"', '' | 
 add-content 'newfile.txt' }