【问题标题】:In Powershell I'm receiving an "OutOfMemoryException" when working with files over 1gb在 Powershell 中,我在处理超过 1gb 的文件时收到“OutOfMemoryException”
【发布时间】:2021-03-04 07:23:33
【问题描述】:

在加载到我的数据仓库之前,我正在做一些文件清理工作,但遇到了文件大小问题:

(Get-Content -path C:\Workspace\workfile\myfile.txt -Raw) -replace '\\"', '"' | Set-Content C:\Workspace\workfile\myfileCLEAN.txt

我的文件大约 2GB。我收到以下错误,不知道如何更正。

Get-Content:“System.OutOfMemoryException”类型的异常是 抛出,........

我不是编码员,但我确实喜欢学习,所以我正在建立自己的数据仓库。因此,如果您确实做出了回应,请记住我的经验水平:)

【问题讨论】:

  • 不要加载整个文件...逐行加载并在每一行上进行替换。然后使用Add-Content 将结果发送到新文件。

标签: powershell file-get-contents


【解决方案1】:

Get-Content 将整个文件加载到内存中。

尝试逐行处理以提高内存利用率。

$infile = "C:\Workspace\workfile\myfile.txt"
$outfile = "C:\Workspace\workfile\myfileCLEAN.txt"

foreach ($line in [System.IO.File]::ReadLines($infile)) {
    Add-Content -Path $outfile -Value ($line -replace '\\"','"')
}

【讨论】:

  • Get-Content 仅在使用-Raw 开关或将调用包含在(...) 中时才将整个文件加载到内存中。虽然[System.IO.File]::ReadLines() 是读取文件的一种有效方式,但在循环中使用Add-Content 会抵消性能优势,循环会在每次迭代中打开和关闭输出文件。
  • 这可行,但与使用 Get-Content 一样慢。我看到的唯一优势是对内存的负担更少,而且我可以更轻松地监控进度。
【解决方案2】:

Get-Content -Raw 使 PowerShell 将整个文件读入单个字符串。

.NET 无法在内存中存储大小超过 2GB 的单个对象,并且字符串中的每个字符占用 2 个字节,因此在读取前约 10 亿个字符后(大致相当于 1GB ASCII 编码的文本文件) ,达到内存限制。

去掉-Raw 开关,-replace 完全可以同时处理多个输入字符串:

(Get-Content -path C:\Workspace\workfile\myfile.txt) -replace '\"', '"' | Set-Content C:\Workspace\workfile\myfileCLEAN.txt

注意-replace 是一个正则表达式 运算符,如果你想从字符串中删除\,你需要转义它:

(Get-Content -path C:\Workspace\workfile\myfile.txt) -replace '\\"', '"' | Set-Content C:\Workspace\workfile\myfileCLEAN.txt

虽然这会起作用,但它仍然会很慢,因为我们仍在将 >2GB 的数据加载到内存中应用-replace 并写入输出文件。

相反,您可能希望将Get-Content 的输出管道ForEach-Object cmdlet:

Get-Content -path C:\Workspace\workfile\myfile.txt |ForEach-Object {
  $_ -replace '\\"','"'
} |Set-Content C:\Workspace\workfile\myfileCLEAN.txt

这允许Get-Content 在完成读取文件之前开始推送输出,因此 PowerShell 不再需要像以前那样分配尽可能多的内存,从而加快执行速度。

【讨论】:

  • 这确实有效,但速度很慢,处理一个 2GB 的文件需要将近一个小时。任何有助于加快进程的提示?
【解决方案3】:
  • 逐行读取文本文件的一种高效方式 - 无需将整个文件加载到内存中 - 是使用 switch 带有 -File 参数的语句。

  • 一种编写文本文件的高效方法是使用System.IO.StreamWriter实例。

  • 正如 Mathias 在 his answer 中指出的那样,逐字使用 \" 和基于 regex-replace operator 实际上替换了 " alone,因为转义正则表达式的规则。虽然您可以使用 '\\"' 解决这个问题,但在这种情况下,更简单且性能更好的替代方法是使用 [string] 类型的 Replace() 方法,该方法对 literal 子字符串进行操作。

把它们放在一起:

# Note: Be sure to use a *full* path, because .NET's working dir. usually
#       differs from PowerShell's.
$streamWriter = [System.IO.StreamWriter]::new('C:\Workspace\workfile\myfileCLEAN.txt')

switch -File C:\Workspace\workfile\myfile.txt {
  default { $streamWriter.WriteLine($_.Replace('\"', '"')) }
}

$streamWriter.Close()

注意:如果您使用的是 版本的 Windows PowerShell,即版本 4 或更低版本,请使用
New-Object System.IO.StreamWriter 'C:\Workspace\workfile\myfileCLEAN.txt'
而不是
[System.IO.StreamWriter]::new('C:\Workspace\workfile\myfileCLEAN.txt')

【讨论】:

  • 感谢您指出这一点。我升级到 v5,哇!这很好用,而且速度超级快 :) 非常感谢!
猜你喜欢
  • 1970-01-01
  • 2011-02-02
  • 1970-01-01
  • 2020-05-18
  • 1970-01-01
  • 2011-02-19
  • 1970-01-01
  • 2015-12-29
  • 2010-10-18
相关资源
最近更新 更多