【问题标题】:Memory errors merging multiple large csv files with Powershell将多个大型 csv 文件与 Powershell 合并时出现内存错误
【发布时间】:2018-06-12 08:37:39
【问题描述】:

对于如何使用 Powershell 将多个 CSV 文件合并为一个的问题,有一些很好的回答,将标题行放在除this thread 中的第一个文件之外的所有文件上。
Kemiller2002 发布的答案在大多数情况下对我来说效果很好,但是当输出文件超过 2GB 时,我开始出现内存不足异常错误。抛出以下错误消息...

Exception of type 'System.OutOfMemoryException' was thrown.
At xxx.ps1:9 char:20
+            $false {$lines | Select -Skip 1}
+                    ~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (:) [], OutOfMemoryException
    + FullyQualifiedErrorId : System.OutOfMemoryException  

这是使用 Powershell 5.1。 MaxMemoryPerShellMB(报告为 2147483647)似乎不是问题,实际系统内存似乎也不是问题 - 我上次运行它时有 33GB 的可用内存(总共 64GB ) 离开。

现在脚本继续运行,并添加到文件中(我的一个最终文件最终大小约为 7GB),但当我看到这个时,我不能确定它是否已捕获所有文件中的每一行弹出错误。

有什么建议吗?

编辑

我添加了一些输出,以便查看错误发生的位置。我正在附加 11 个文件,大小从大约 350 MB 到 1GB 不等……正是这两个大约 1GB 的文件导致了错误。一个报告的长度为 909,050,983,另一个为 973,429,260。

【问题讨论】:

  • 嗯,最简单的方法是确保您运行的是 64 位 powershell 实例而不是 32 位实例。
  • 是的,对不起,我应该提到它运行在 64 位

标签: powershell csv


【解决方案1】:

我没有要测试的大文件,但使用 .net 方法可能是一种替代方法,因为您一次只能处理 1 行,而不是将整个文件加载到内存中。

$filepath = "c:\temp"
$outputfile = "c:\temp\output\result.csv"
$encoding = [System.Text.Encoding]::UTF8

$files = Get-ChildItem -Path $filePath -Filter *.csv

$w = New-Object System.IO.StreamWriter($outputfile, $true, $encoding)

$skiprow = $false
foreach ($file in $files)
{
    $r = New-Object System.IO.StreamReader($file.fullname, $encoding)
    while (($line = $r.ReadLine()) -ne $null) 
    {
        if (!$skiprow)
        {
            $w.WriteLine($line)
        }
        $skiprow = $false
    }
    $r.Close()
    $r.Dispose()
    $skiprow = $true
}

$w.close()
$w.Dispose()

【讨论】:

  • 我明天试试。
  • ...但我认为那会很慢。使用 UltraEdit 手动编辑标题可能更有效,然后将文件复制在一起,然后添加回标题行。哦!当然,我也可以编写这些手动组件的脚本 - 将第一个文件中的标头复制到临时文件中,从所有文件中剥离标头,将它们一起复制到临时主体文件中,然后合并临时头文件和临时主体文件.
  • 哇...实际上只是为了让它在一夜之间运行而启动了您的脚本,事实证明它比我使用的其他脚本效率更高。另一个脚本处理这 11 个文件大约需要 40 分钟,而这里只用了 10 分钟,您的脚本已完成 75%。毫不犹豫地翻阅了两个大文件。
  • 抱歉,我会投票赞成你的答案以将其标记为答案,但我没有足够的声誉。
  • 如何在其中设置编码选项?我的源文件中的口音都搞砸了。我知道有一种方法可以为 StreamWriter 设置编码,但我不确定如何以您调用它的方式进行。
【解决方案2】:

有些人使用这种方法做这件事的方式简直太疯狂了......

Get-Content $SrcFile1, $SrcFile2 | Set-Content $DstFile

不要那样做!它非常慢并且总是导致内存异常错误。而是使用命令处理器中的旧文件副本,例如...

cmd /c "copy $($SrcFile1) + $($SrcFile2) $($DstFile)"

【讨论】:

    【解决方案3】:

    从 Bob 提出的重要观点演变而来的完整答案

    <###########################
    user config section
    ###########################>
    
    # location of files to concatenate
    $sourcefolder = "P:\DWH\ntm_v1\uncompressed"
    
    # source file extension
    $ext = "*.csv"
    
    # output folder (best to have new folder for safety)
    $outfolder = $sourcefolder + "\..\concatenated"
    
    #output file name
    $outfilename = "concat.txt"
    
    <###########################
    do work
    ###########################>
    
    # build full path to out file
    $concatfile = $outfolder + "\" + $outfilename
    
    #create output folder
    md -Force $outfolder
    
    # delete output file if exists
    if (Test-Path $concatfile) 
    {
      Remove-Item -Confirm $concatfile
    }
    
    ForEach ($file in (Get-ChildItem -Path $sourcefolder -Filter $ext)) {
        $param = "type $file >> $concatfile"
        Write-Host "cmd /c $param"
            
        # run concat command
        cmd /c $param;  
    }
    

    【讨论】:

      猜你喜欢
      • 2020-08-21
      • 1970-01-01
      • 1970-01-01
      • 2018-11-08
      • 1970-01-01
      • 2016-03-19
      • 2014-12-10
      • 2020-03-19
      • 1970-01-01
      相关资源
      最近更新 更多