PowerShellでCSVファイルを第1正規化する ― 2017/04/30 07:45
PowerShellを使うと(コードさえ分かっていれば)CSVファイルを簡単に処理することができます。
自分で一からCSVファイルを処理するなんて言ったらクソめんどくさいしバグまみれでやる気になりません。
foreach ($l in Get-Content file.csv) {$l -split ","}
するだけじゃない、と思われた方は、値に「,」や「"」が含まれている場合やセル内改行されている場合を想定してみましょう。
PowerShellの場合にはImport-CSVとExport-CSVがあるので、簡単に処理できてしまいます。
簡単な処理についてはselect-objectで記述できるのですが、1行処理して複数行を生成するような処理にはforeach-objectが必要ではないかと思います。
問題はforeach-objectの処理結果をExport-CSVに渡す方法が不明だったこと。いえ、簡単に渡す方法が不明だったこと。
以前参考にさせていただいたのはこちら
http://www.vwnet.jp/Windows/PowerShell/CreateCustomObject.htm
できることはできるのですが、ちょっとコードがわかりにくい。3.0より前はこういう書き方しかできなかったのかもしれませんが。
今回とても参考になったのはこちら
http://tech.guitarrapc.com/entry/2013/03/20/170321
なるほど、ハッシュテーブルをキャスト一発でExport-CSVに渡せる形にできそうです。
Export-CSVが PSCustomObject を処理する(できる?)というのはまた別のサイトで知った気がします。
で、コードです。
[string]$uDesktop = [Environment]::GetFolderPath("Desktop")
[string]$uUnNormalizedFile = ( Join-Path $uDesktop "UnNormalized.csv" )
[string]$uNormalizedFile = ( Join-Path $uDesktop "Normalized.csv" )
Import-Csv $uUnNormalizedFile -Encoding Default |
ForEach-Object -Process {
for ( $i = 1; $i -le 10; $i++ )
{
$uRow = @{}
$uRow.請求書番号 = $_.請求書番号
$uRow.金額 = $_."金額$i"
[PSCustomObject]$uRow
}
} |
Export-Csv $uNormalizedFile -Encoding Default -NoTypeInformation
元データはこんな感じ。
請求書番号,金額1,金額2,金額3,金額4,金額5,金額6,金額7,金額8,金額9,金額10
17-0001,100,101,102,103,104,105,106,107,108,109
17-0002,200,201,202,203,204,205,206,207,208,209
処理結果はこんな感じ
"請求書番号","金額"
"17-0001","100"
"17-0001","101"
"17-0001","102"
"17-0001","103"
"17-0001","104"
"17-0001","105"
"17-0001","106"
"17-0001","107"
"17-0001","108"
"17-0001","109"
"17-0002","200"
"17-0002","201"
"17-0002","202"
"17-0002","203"
"17-0002","204"
"17-0002","205"
"17-0002","206"
"17-0002","207"
"17-0002","208"
"17-0002","209"
無事に正規化できました。
上記コードは漫然と正規化していますが、実際のコードの場合には、以下のように金額に値があるかどうかを判別することになるでしょう。
Import-Csv $uUnNormalizedFile -Encoding Default |
ForEach-Object -Process {
for ( $i = 1; $i -le 10; $i++ )
{
if ( $_."金額$i" -ne "" )
{
$uRow = @{}
$uRow.請求書番号 = $_.請求書番号
$uRow.金額 = $_."金額$i"
[PSCustomObject]$uRow
}
}
} |
Export-Csv $uNormalizedFile -Encoding Default -NoTypeInformation
元データ
請求書番号,金額1,金額2,金額3,金額4,金額5,金額6,金額7,金額8,金額9,金額10
17-0001,100,101,102,103,104,105,106,,,
17-0002,200,201,202,203,204,,,,,
結果
"請求書番号","金額"
"17-0001","100"
"17-0001","101"
"17-0001","102"
"17-0001","103"
"17-0001","104"
"17-0001","105"
"17-0001","106"
"17-0002","200"
"17-0002","201"
"17-0002","202"
"17-0002","203"
"17-0002","204"
自分で一からCSVファイルを処理するなんて言ったらクソめんどくさいしバグまみれでやる気になりません。
foreach ($l in Get-Content file.csv) {$l -split ","}
するだけじゃない、と思われた方は、値に「,」や「"」が含まれている場合やセル内改行されている場合を想定してみましょう。
PowerShellの場合にはImport-CSVとExport-CSVがあるので、簡単に処理できてしまいます。
簡単な処理についてはselect-objectで記述できるのですが、1行処理して複数行を生成するような処理にはforeach-objectが必要ではないかと思います。
問題はforeach-objectの処理結果をExport-CSVに渡す方法が不明だったこと。いえ、簡単に渡す方法が不明だったこと。
以前参考にさせていただいたのはこちら
http://www.vwnet.jp/Windows/PowerShell/CreateCustomObject.htm
できることはできるのですが、ちょっとコードがわかりにくい。3.0より前はこういう書き方しかできなかったのかもしれませんが。
今回とても参考になったのはこちら
http://tech.guitarrapc.com/entry/2013/03/20/170321
なるほど、ハッシュテーブルをキャスト一発でExport-CSVに渡せる形にできそうです。
Export-CSVが PSCustomObject を処理する(できる?)というのはまた別のサイトで知った気がします。
で、コードです。
[string]$uDesktop = [Environment]::GetFolderPath("Desktop")
[string]$uUnNormalizedFile = ( Join-Path $uDesktop "UnNormalized.csv" )
[string]$uNormalizedFile = ( Join-Path $uDesktop "Normalized.csv" )
Import-Csv $uUnNormalizedFile -Encoding Default |
ForEach-Object -Process {
for ( $i = 1; $i -le 10; $i++ )
{
$uRow = @{}
$uRow.請求書番号 = $_.請求書番号
$uRow.金額 = $_."金額$i"
[PSCustomObject]$uRow
}
} |
Export-Csv $uNormalizedFile -Encoding Default -NoTypeInformation
元データはこんな感じ。
請求書番号,金額1,金額2,金額3,金額4,金額5,金額6,金額7,金額8,金額9,金額10
17-0001,100,101,102,103,104,105,106,107,108,109
17-0002,200,201,202,203,204,205,206,207,208,209
処理結果はこんな感じ
"請求書番号","金額"
"17-0001","100"
"17-0001","101"
"17-0001","102"
"17-0001","103"
"17-0001","104"
"17-0001","105"
"17-0001","106"
"17-0001","107"
"17-0001","108"
"17-0001","109"
"17-0002","200"
"17-0002","201"
"17-0002","202"
"17-0002","203"
"17-0002","204"
"17-0002","205"
"17-0002","206"
"17-0002","207"
"17-0002","208"
"17-0002","209"
無事に正規化できました。
上記コードは漫然と正規化していますが、実際のコードの場合には、以下のように金額に値があるかどうかを判別することになるでしょう。
Import-Csv $uUnNormalizedFile -Encoding Default |
ForEach-Object -Process {
for ( $i = 1; $i -le 10; $i++ )
{
if ( $_."金額$i" -ne "" )
{
$uRow = @{}
$uRow.請求書番号 = $_.請求書番号
$uRow.金額 = $_."金額$i"
[PSCustomObject]$uRow
}
}
} |
Export-Csv $uNormalizedFile -Encoding Default -NoTypeInformation
元データ
請求書番号,金額1,金額2,金額3,金額4,金額5,金額6,金額7,金額8,金額9,金額10
17-0001,100,101,102,103,104,105,106,,,
17-0002,200,201,202,203,204,,,,,
結果
"請求書番号","金額"
"17-0001","100"
"17-0001","101"
"17-0001","102"
"17-0001","103"
"17-0001","104"
"17-0001","105"
"17-0001","106"
"17-0002","200"
"17-0002","201"
"17-0002","202"
"17-0002","203"
"17-0002","204"
コメントをどうぞ
※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。
※なお、送られたコメントはブログの管理者が確認するまで公開されません。
※投稿には管理者が設定した質問に答える必要があります。