在 Powershell 中,我在处理超过 1gb 的文件时收到 "OutOfMemoryException"

In Powershell I'm receiving an "OutOfMemoryException" when working with files over 1gb

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

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

我的文件大约有 2GB。我收到以下错误,但不确定如何更正。

Get-Content : Exception of type 'System.OutOfMemoryException' was thrown, ........

我不是编码员,但我喜欢学习,所以正在构建自己的数据仓库。所以如果你真的回复了,请记住我的经验水平:)

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 使 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 是一个 regex 运算符,如果你想从字符串中删除 \,你需要转义它:

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

虽然这会起作用,但它仍然会很慢,因为我们仍在将 >2GB 的数据加载到内存中 before 应用 -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 不再需要像以前那样分配那么多内存,从而加快执行速度。

  • 一种逐行读取文本文件的高效方法 - 无需将整个文件加载到内存中 -是使用带有 -File 参数的 switch 语句。

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

  • 正如 Mathias 在 , using verbatim \" with the regex-based actually replaces " alone, due to the escaping rules of regexes. While you could address that with '\"', in this case a simpler and better-performing alternative is to use the [string] type's Replace() 方法中指出的那样,该方法对 文字 子字符串进行操作。

将它们放在一起:

# 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')