获取内容的替代方法
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%)。
为了获得更好的性能,请使用 建议的 StreamReader
和 StreamWriter
方法:
$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'
我目前有以下代码行。
(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'
这样做可以避免您观察到的内存耗尽。按照
为了获得更好的性能,请使用 StreamReader
和 StreamWriter
方法:
$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'