9

私はいくつかのETL作業にPowershellを使用しており、圧縮されたテキストファイルを読み込んで、各行の最初の3文字に応じて分割しています。

入力ファイルをフィルタリングするだけの場合は、フィルタリングされたストリームをOut-Fileにパイプして、それで処理できます。しかし、出力を複数の宛先にリダイレクトする必要があります。私が知る限り、これは単純なパイプでは実行できません。すでに.NETストリームリーダーを使用して圧縮された入力ファイルを読み取っていますが、ストリームライターを使用して出力ファイルを書き込む必要があるかどうか疑問に思っています。

ナイーブバージョンは次のようになります。

while (!$reader.EndOfFile) {
  $line = $reader.ReadLine();
  switch ($line.substring(0,3) {
    "001" {Add-Content "output001.txt" $line}
    "002" {Add-Content "output002.txt" $line}
    "003" {Add-Content "output003.txt" $line}
    }
  }

これは悪いニュースのように見えます。ファイルを1行に1回検索、開く、書き込む、閉じるということです。入力ファイルは500MB以上の巨大なモンスターです。

これをPowershell構造で効率的に処理する慣用的な方法はありますか、それとも.NETストリームライターを使用する必要がありますか?

これに使用できる(New-Item "path" -type "file")オブジェクトのメソッドはありますか?

コンテキストの編集:

DotNetZipライブラリを使用してZIPファイルをストリームとして読み取ります。したがって、 /streamreaderではなく。サンプルコード:Get-Contentgc

[System.Reflection.Assembly]::LoadFrom("\Path\To\Ionic.Zip.dll") 
$zipfile = [Ionic.Zip.ZipFile]::Read("\Path\To\File.zip")

foreach ($entry in $zipfile) {
  $reader = new-object system.io.streamreader $entry.OpenReader();
  while (!$reader.EndOfFile) {
    $line = $reader.ReadLine();
    #do something here
  }
}

私はおそらくDispose()$zipfileと$readerの両方を使用する必要がありますが、それは別の質問です!

4

2 に答える 2

14

読む

ファイルの読み取りと解析に関しては、次のswitchステートメントを使用します。

switch -file c:\temp\stackoverflow.testfile2.txt -regex {
  "^001" {Add-Content c:\temp\stackoverflow.testfile.001.txt $_}
  "^002" {Add-Content c:\temp\stackoverflow.testfile.002.txt $_}
  "^003" {Add-Content c:\temp\stackoverflow.testfile.003.txt $_}
}

私はそれがより良いアプローチだと思います

  • 正規表現のサポートがあり、部分文字列を作成する必要はありません(高価な場合があります)。
  • パラメータ -fileは非常に便利です;)

書き込み

出力の書き込みについては、streamwriterを使用してテストしますが、のパフォーマンスAdd-Contentが適切であれば、それに固執します。

追加:キースは演算子の使用を提案しまし>>たが、非常に遅いようです。それに加えて、ファイルサイズを2倍にするUnicodeで出力を書き込みます。

私のテストを見てください:

[1]: (measure-command {
>>     gc c:\temp\stackoverflow.testfile2.txt  | %{$c = $_; switch ($_.Substring(0,3)) {
>>             '001'{$c >> c:\temp\stackoverflow.testfile.001.txt} `
>>             '002'{$c >> c:\temp\stackoverflow.testfile.002.txt} `
>>             '003'{$c >> c:\temp\stackoverflow.testfile.003.txt}}}
>> }).TotalSeconds
>>
159,1585874
[2]: (measure-command {
>>     gc c:\temp\stackoverflow.testfile2.txt  | %{$c = $_; switch ($_.Substring(0,3)) {
>>             '001'{$c | Add-content c:\temp\stackoverflow.testfile.001.txt} `
>>             '002'{$c | Add-content c:\temp\stackoverflow.testfile.002.txt} `
>>             '003'{$c | Add-content c:\temp\stackoverflow.testfile.003.txt}}}
>> }).TotalSeconds
>>
9,2696923

違いは大きいです。

比較のために:

[3]: (measure-command {
>>     $reader = new-object io.streamreader c:\temp\stackoverflow.testfile2.txt
>>     while (!$reader.EndOfStream) {
>>         $line = $reader.ReadLine();
>>         switch ($line.substring(0,3)) {
>>             "001" {Add-Content c:\temp\stackoverflow.testfile.001.txt $line}
>>             "002" {Add-Content c:\temp\stackoverflow.testfile.002.txt $line}
>>             "003" {Add-Content c:\temp\stackoverflow.testfile.003.txt $line}
>>             }
>>         }
>>     $reader.close()
>> }).TotalSeconds
>>
8,2454369
[4]: (measure-command {
>>     switch -file c:\temp\stackoverflow.testfile2.txt -regex {
>>         "^001" {Add-Content c:\temp\stackoverflow.testfile.001.txt $_}
>>         "^002" {Add-Content c:\temp\stackoverflow.testfile.002.txt $_}
>>         "^003" {Add-Content c:\temp\stackoverflow.testfile.003.txt $_}
>>     }
>> }).TotalSeconds
8,6755565

追加:筆記のパフォーマンスに興味がありました..少し驚きました

[8]: (measure-command {
>>     $sw1 = new-object io.streamwriter c:\temp\stackoverflow.testfile.001.txt3b
>>     $sw2 = new-object io.streamwriter c:\temp\stackoverflow.testfile.002.txt3b
>>     $sw3 = new-object io.streamwriter c:\temp\stackoverflow.testfile.003.txt3b
>>     switch -file c:\temp\stackoverflow.testfile2.txt -regex {
>>         "^001" {$sw1.WriteLine($_)}
>>         "^002" {$sw2.WriteLine($_)}
>>         "^003" {$sw3.WriteLine($_)}
>>     }
>>     $sw1.Close()
>>     $sw2.Close()
>>     $sw3.Close()
>>
>> }).TotalSeconds
>>
0,1062315

80倍高速です。ここで、決定する必要があります。速度が重要な場合は、を使用してStreamWriterください。コードの明確さが重要な場合は、を使用してAdd-Contentください。


部分文字列と正規表現

キースによると、サブストリングは20%高速です。いつものように、それは異なります。ただし、私の場合、結果は次のようになります。

[102]: (measure-command {
>>     gc c:\temp\stackoverflow.testfile2.txt  | %{$c = $_; switch ($_.Substring(0,3)) {
>>             '001'{$c | Add-content c:\temp\stackoverflow.testfile.001.s.txt} `
>>             '002'{$c | Add-content c:\temp\stackoverflow.testfile.002.s.txt} `
>>             '003'{$c | Add-content c:\temp\stackoverflow.testfile.003.s.txt}}}
>> }).TotalSeconds
>>
9,0654496
[103]: (measure-command {
>>     gc c:\temp\stackoverflow.testfile2.txt  | %{$c = $_; switch -regex ($_) {
>>             '^001'{$c | Add-content c:\temp\stackoverflow.testfile.001.r.txt} `
>>             '^002'{$c | Add-content c:\temp\stackoverflow.testfile.002.r.txt} `
>>             '^003'{$c | Add-content c:\temp\stackoverflow.testfile.003.r.txt}}}
>> }).TotalSeconds
>>
9,2563681

したがって、違いは重要ではなく、私にとって、正規表現はより読みやすくなっています。

于 2010-01-29T06:06:30.137 に答える
3

入力ファイルのサイズを考えると、一度に1行ずつ処理する必要があります。出力ファイルを再度開いたり閉じたりしても、パフォーマンスが大幅に低下するとは思いません。それは確かにワンライナーとしてもパイプラインを使用して実装を可能にします-実際にはあなたの実装とそれほど違いはありません。水平スクロールバーを取り除くために、ここでラップしました。

gc foo.log | %{switch ($_.Substring(0,3)) {
    '001'{$input | out-file output001.txt -enc ascii -append} `
    '002'{$input | out-file output002.txt -enc ascii -append} `
    '003'{$input | out-file output003.txt -enc ascii -append}}}
于 2010-01-29T04:37:20.567 に答える