Edited at

Excel から出力された汚い CSV を読み込むための定型文

More than 1 year has passed since last update.

Excel などからエクスポートした CSV は非常識なフォーマットであることが多く、Import-CSV を以ってしても読み込みに失敗することが少なくない。

そのような場合に、CSV 表のレイアウトが変更されていないかチェックしつつ、上手く読み込む方法をメモしておく。

$organized_csv_data = cat .\chaotically_separated_values.csv |

ForEach-Object -begin {
$i = 0
} -process {
$i += 1

# ヘッダのバリデーション
if ($i -eq 1) { if ($_ -eq '$i 行目に想定される値') { return } else { throw "$i 行目が変更されています" } }
# ... 必要なだけバリデーション行の繰り返し

# 本来こうであって欲しいという CSV ヘッダ
if ($i -eq $EndOfHeaderLines) {'問題なかった場合に生成するヘッダ'}

# データ部分はそのままパイプに流す
$_
} |
Out-String |
ConvertFrom-CSV

cat で csv を読み込むと文字列の配列になり ConvertFrom-CSV では上手く処理できないことがあるため、Out-String を挟んでいる。

Excel のまま vba でやったらでええやん、という意見もあると思うが、バッチ的に処理できるメリットは代えがたいものがある。

関数にするとこんな感じになると思う。

Function Test-CSV {

param(
[scriptblock]$Expects,
[scriptblock]$Headers
)
begin {
$ExpectStrings = & $Expects
$i = 0
$len = $ExpectStrings.length
}
process {
$i += 1
$Actual = $_
if ($i -le $len) {
if ($ExpectStrings[$i-1] -eq $Actual) {
return
} else {
throw "Format Error: line $i `nExpect: $($ExpectStrings[$i]) `nActual: $Actual"
}
} elseif ($i -eq $len+1) {
& $Headers
$Actual
} else {
$Actual
}
}
}

# 使い方
$csv_data = cat .\chaotically_separated_values.csv |
Test-CSV -Expects {
'一行目に期待する文字列'
'二行目に期待する文字列'
...
} -Headers {
'列名1,列名2,...'
} |
Out-String |
ConvertFrom-CSV