获取内容的替代方法

Alternative to Get-Content

我目前有以下代码行。

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

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

有没有更好的方法可以在不增加相同开销的情况下获得相同的结果?

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

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

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

使用流来读取文件,这样就不会全部放到内存中,也可以使用流来写入输出。这应该表现得很好,并降低内存使用率:

$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()

您的问题不是由 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'

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

为了获得更好的性能,请使用 建议的 StreamReaderStreamWriter 方法:

$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'