PowerShellで巨大なファイルをGet-Contentし、Export-Csvするのを省メモリで行う

Facebook にシェア
[`yahoo` not found]
[`livedoor` not found]
[`friendfeed` not found]

PowerShellで、ファイルを加工し、CSVファイルに出力する、ということを行った。

しかし、Get-Contentで取得したファイルを、下記の様にそのまま変数に突っ込むとファイル容量の数倍のメモリを確保しまう、というPowerShellの仕様がわかった。

$inputfile="c:\tmp\input.txt"

$inputdata=Get-Content $inputfile -Encoding UTF8
foreach($line in $inputdata){
 いろんな処理~
}

しかも、配列を追加していく処理は重いらしく、かなり時間がかかる。

開発段階では、元のファイルサイズが1MB程度だったので気がつきませんでしたが、500MBや800MBになると、とんでもなくメモリを食っていました。

配列を確保しないで、次の処理を行わせるようにするには、パイプで繋いでForEach-Objectで回します。

$inputfile="c:\tmp\input.txt"

Get-Content $inputfile -Encoding UTF8 | ForEach-Object {
  $line=$_
 いろんな処理~
}

これにより、Get-Contentで読み込んだファイルに対するメモリ確保は行わずに、次の処理を行わせることができるようになります。

次に、各行を処理した結果をcsvファイルで出力する方法について改善します。

例えば、下記の様に出力結果を$results配列に入れ、それをまとめてexport-csvする、というのが簡単な実装でしょう。

$inputfile="c:\tmp\input.txt"
$outputfile="c:\tmp\output.csv"

$results=Get-Content $inputfile -Encoding UTF8 | ForEach-Object {
  $line=$_
  いろんな処理~
  $output = New-Object -TypeName PSObject
  $output | Add-Member -MemberType NoteProperty -Name "Name" -Value $name
  $output | Add-Member -MemberType NoteProperty -Name "Value" -Value $value
  $output | Add-Member -MemberType NoteProperty -Name "Details" -Value $details
  $output
}
$results | Export-Csv $outputfile -Encoding UTF8 -NoTypeInformation -Append -NoClobber

これも、input側と同じく、配列のメモリ確保問題があります。

しかし、Export-Csvは、配列を使わないと出力できませんし、ファイルの入出力処理を毎行実行するにはコストが高いです。
1000行ぐらいであればメモリへの影響も少なく、また、1回当たりの書き込み負荷もそれほどではないようなので、1000行に1回、Export-Csvを実行するようにしたものが、下記です。

$inputfile="c:\tmp\input.txt"
$outputfile="c:\tmp\output.csv"

$results=@()
$linecount=0
Get-Content $inputfile -Encoding UTF8 | ForEach-Object {
  $line=$_
  いろんな処理~
  $output = New-Object -TypeName PSObject
  $output | Add-Member -MemberType NoteProperty -Name "Name" -Value $name
  $output | Add-Member -MemberType NoteProperty -Name "Value" -Value $value
  $output | Add-Member -MemberType NoteProperty -Name "Details" -Value $details
  $output

  $linecount++
  if(($linecount % 1000) -eq 0 ){
    $results | Export-Csv $outputfile -Encoding UTF8 -NoTypeInformation -Append -NoClobber
    $results = @()
  }
}
$results | Export-Csv $outputfile -Encoding UTF8 -NoTypeInformation -Append -NoClobber

これにより、読み込ませるファイルが大きい場合でも、PowerShellが使用するメモリ容量を少なく抑えることが可能となりました。

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>