获取内容的替代方案

ric*_*y89 2 powershell out-of-memory

我目前有以下代码行.

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

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

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

似乎我正在做最佳实践的反面但不确定还有什么比上面更清洁/更少的RAM密集.

cam*_*.rw 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()
Run Code Online (Sandbox Code Playgroud)

  • 只是想通了如果它遇到一个空行,就会退出那里. (3认同)

Ans*_*ers 5

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

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

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'
Run Code Online (Sandbox Code Playgroud)

这样做可以避免您观察到的内存耗尽.通过增加@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'
Run Code Online (Sandbox Code Playgroud)

  • .peek()方法用于测试你的EOF是否在遇到空行时没有结束循环的问题. (2认同)