CSVの囲み文字とエスケープ文字を置換する

CSVファイルのデータフィールドに囲み文字が含まれている場合、Excelに取り込もうとすると崩れてしまう。
通常は囲み文字と区別するために、データフィールド内の囲み文字をエスケープしたりする。
このようなCSVを正常に取り込むため、囲み文字を別文字に置換することを考える。
また、エスケープ文字はもはや不要なので削除する方法を考える。
ここでは、囲み文字のダブルクォーテーションをチルダに置換するものとする。

まずサンプルCSV(in.csv)の内容を確認。

1$ cat in.csv
2"aaa","bb\"","\\cc"

次に、このサンプルCSV内の","~,~に置換する。

1$ sed -e 's/\",\"/~,~/g' in.csv
2"aaa~,~bb\"~,~\\cc"

次に、行頭の"~に置換する処理を追加。

1$ sed -e 's/\",\"/~,~/g' -e 's/^\"/~/' in.csv
2~aaa~,~bb\"~,~\\cc"

次に、行末の"~に置換する処理を追加。

1$ sed -e 's/\",\"/~,~/g' -e 's/^\"/~/' -e 's/\"$/~/' in.csv
2~aaa~,~bb\"~,~\\cc~

次に、\"のエスケープ文字\を削除する処理を追加。

1$ sed -e 's/\",\"/~,~/g' -e 's/^\"/~/' -e 's/\"$/~/' -e 's/\\"/"/g' in.csv
2~aaa~,~bb"~,~\\cc~

次に、\\のエスケープ文字\を削除する処理を追加。

1$ sed -e 's/\",\"/~,~/g' -e 's/^\"/~/' -e 's/\"$/~/' -e 's/\\"/"/g' -e 's/\\\\/\\/g' in.csv
2~aaa~,~bb"~,~\cc~

これにてExcelなどに取り込むときに、囲み文字として~を指定することで、正常に取り込める。

また、以下のようにパイプで繋げても同じ結果を得られる。

1$ sed -e 's/\",\"/~,~/g' in.csv | sed -e 's/^\"/~/' | sed -e 's/\"$/~/' | sed -e 's/\\"/"/g' | sed -e 's/\\\\/\\/g'
2~aaa~,~bb"~,~\cc~

さて、ここからは本題からずれるが、上記2つの方法でどちらが処理時間が短くなるか、試したくなった。
まず、上のサンプル(in.csv)をもとに、新たに10,000,000行のサンプルファイル(bigIn.csv)を作成した。
なお、このbigIn.csvの各行は全く同じである。
1$ cat in.csv | awk '{for(i=1; i<=10000000; i++){print $0}}' > bigIn.csv

この新サンプル(bigIn.csv)に対して、一つ目の方法で処理時間を計測。

1$ time sed -e 's/\",\"/~,~/g' -e 's/^\"/~/' -e 's/\"$/~/' -e 's/\\"/"/g' -e 's/\\\\/\\/g' bigIn.csv > /dev/null
2
3real	0m15.619s
4user	0m15.522s
5sys	0m0.087s

二つ目のパイプを使う方法での結果は以下。

1$ time sed -e 's/\",\"/~,~/g' bigIn.csv | sed -e 's/^\"/~/' | sed -e 's/\"$/~/' | sed -e 's/\\"/"/g' | sed -e 's/\\\\/\\/g' > /dev/null
2
3real	0m6.800s
4user	0m21.678s
5sys	0m0.405s

理由は不明だが、パイプを使った方が少し早くなった。
なお、timeコマンドの表示内容の確認はUNIX/Linuxの部屋 timeコマンドの使い方を参考にした。
このサイトに以下の記述がある。

それぞれの値の関係性はreal = user + sys + I/O 待ち時間である。
ただしマルチコア CPU だと user と sys が real を超える可能性がある。

パイプの方法でuserがrealより大きくなっているのは、マルチコアシステムで各コアでの所要時間が加算されたためだろうか。

関連ページ