powershell - [System.IO.File]::ReadAllText 上的内存不足异常与大 CSV
问题描述
我有一个简单的 PowerShell 脚本,将“false”或“true”替换为“0”或“1”:
$InputFolder = $args[0];
if($InputFolder.Length -lt 3)
{
Write-Host "Enter a path name as your first argument" -foregroundcolor Red
return
}
if(-not (Test-Path $InputFolder)) {
Write-Host "File path does not appear to be valid" -foregroundcolor Red
return
}
Get-ChildItem $InputFolder
$content = [System.IO.File]::ReadAllText($InputFolder).Replace("`"false`"", "`"0`"").Replace("`"true`"", "`"1`"").Replace("`"FALSE`"", "`"0`"").Replace("`"TRUE`"", "`"1`"")
[System.IO.File]::WriteAllText($InputFolder, $content)
[GC]::Collect()
这适用于我必须修改的几乎所有文件,除了一个 808MB CSV。我不知道这个 CSV 中有多少行,因为我没有任何东西可以正确打开它。
有趣的是,当通过 PowerShell 直接或通过命令提示符手动调用时,PowerShell 脚本将成功完成。当它作为所需的 SSIS 包的一部分启动时,就会发生错误。
文件的样本数据:
"RowIdentifier","DateProfileCreated","IdProfileCreatedBy","IDStaffMemberProfileRole","StaffRole","DateEmploymentStart","DateEmploymentEnd","PPAID","GPLocalCode","IDStaffMember","IDOrganisation","GmpID","RemovedData"
"134","09/07/1999 00:00","-1","98","GP Partner","09/07/1999 00:00","14/08/2009 15:29","341159","BRA 871","141","B83067","G3411591","0"
抛出的错误信息:
我不依赖于 PowerShell - 我对其他选项持开放态度。我以前有一个 C# 脚本,但是在比这小的文件上死了 - 我不是 C# 开发人员,所以根本无法调试它。
任何建议或帮助都非常感激。
解决方案
通常,避免一次全部读取大文件,因为您可能会遇到内存不足的情况。
相反,逐行处理基于文本的文件- 读取和写入。
虽然 PowerShell 通常擅长逐行(逐个对象)处理,但它处理多行文件时速度很慢。
直接使用 .NET Framework(虽然更复杂)提供了更好的性能。
如果逐行处理输入文件,则不能直接写回它,而必须写入临时输出文件,成功时可以将输入文件替换为该文件。
这是出于性能原因直接使用 .NET 类型的解决方案:
# Be sure to use a *full* path, because .NET typically doesn't have the same working dir. as PS.
$inFile = Convert-Path $Args[0]
$tmpOutFile = [io.path]::GetTempFileName()
$tmpOutFileWriter = [IO.File]::CreateText($tmpOutFile)
foreach ($line in [IO.File]::ReadLines($inFile)) {
$tmpOutFileWriter.WriteLine(
$line.Replace('"false"', '"0"').Replace('"true"', '"1"').Replace('"FALSE"', '"0"').Replace('"TRUE"', '"1"')
)
}
$tmpOutFileWriter.Dispose()
# Replace the input file with the temporary file.
# !! BE SURE TO MAKE A BACKUP COPY FIRST.
# -WhatIf *previews* the move operation; remove it to perform the actual move.
Move-Item -Force -LiteralPath $tmpOutFile $inFile -WhatIf
笔记:
假定为 UTF-8 编码,并且重写的文件不会有 BOM。您可以通过为 .NET 方法指定所需的编码来更改此设置。
顺便说一句:
.Replace()
每个输入行上的调用链可以简化如下,使用不区分大小写-replace
的 PowerShell运算符,因此只需要2 次替换: 然而,虽然写起来更短,但它实际上比调用链,大概是因为它是基于正则表达式的,这会导致额外的处理。
$line -replace '"false"', '"0"' -replace '"true"', '"1"'
.Replace()
-replace
推荐阅读
- javascript - 可以初始化动态模块并将它们联合起来的 Api 类
- python - mysql.connector.errors.ProgrammingError:1054(42S22):'where 子句'中的未知列'X'
- fiware-orion - FIWARE Orion:更改实体类型
- smtp - 我的代码导致 STARTTLS 出现错误,我该如何解决?
- xcode - React Native 项目不在当前的 IOS 模拟器或 Xcode 上
- java - 如何使用 Opencv 将 PNG 从 Java 类传递到 Android 中的 Native 类
- c++ - 为什么可变参数模板在 C++ 中的行为是这样的?
- reactjs - TypeError: this.props.sendKitchen 不是一个函数……为什么?
- android - findViewById 似乎没有返回正确的视图类型?
- php - 未找到 Laravel 干预 ImageServiceProvider