2012年11月28日水曜日

◆バイナリファイルの読み書き

Get-ContentコマンドレットのEncodingパラメータに「Byte」を指定するとバイナリーでの読み込みができることに最近気づいた。

Set-Contentに同様の指定をすると書き込みもOKだ。

001
002

[Byte[]]$bFile = Get-Content "F:\Desktop\NorthwindJ.mdf" -Encoding Byte
Set-Content "F:\Desktop\NorthwindJ2.mdf" -Value $bFile -Encoding Byte

これだけならばめでたしめでたしなのだが、安心してちょっと大きめのファイル(といっても数M程度)を指定すると大変なことになってしまう。

Get-Content : 種類 'System.OutOfMemoryException' の例外がスローされました。

発生場所 C:\Users\kumagai_mitsugu\AppData\Local\Temp\efd7ec81-99b3-4bc1-804a-b983b38f7e31.ps1:1 文字:29
+ [Byte[]]$bFile = Get-Content <<<<  "F:\Desktop\NorthwindJ.mdf" -Encoding Byte
    + CategoryInfo          : NotSpecified: (:) [Get-Content]、OutOfMemoryException
    + FullyQualifiedErrorId : System.OutOfMemoryException,Microsoft.PowerShell.Commands.GetContent
   Command

数M程度のファイルもまともに扱えない機能って?と思ったりもする。

そもそも「Byte」での読み込みは非常に遅い。
どうやら本当に「Byte」単位で読んでいるらしい。

そこで、読み込みのバッファとして「ReadCount」パラメータに「10KB」とかを適当に指定してあげる。

>Get-Content "F:\Desktop\NorthwindJ.mdf" -Encoding Byte -ReadCount 10KB

すると読み込みは一瞬で終わるようにはなるのだが、読み込まれた形式が「10KB」の配列になってしまう。
例えば、ファイルが100KBだとすると10個の10KBのobject配列が出来上がってしまう。

結局「Set-Content」するためにはそれらをByte配列にする必要があるので、そこに時間がかかり元の木阿弥となる。

もう少し何か方法が有るのかもしれないが、.Netframeworkクラスを使えば単純に出来る処理が、コマンドレットを使うと面倒になるようでは本末転倒である。

という事で、これまで通り以下の方法で行くこととした。

001
002

$src = [System.IO.File]::ReadAllBytes("F:\Desktop\NorthwindJ.mdf")
[System.IO.File]::WriteAllBytes("F:\Desktop\NorthwindJ2.mdf",$src)

0 件のコメント:

コメントを投稿