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

2023年6月4日更新

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

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

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

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

1$ sed '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~

なお、上記の行頭・行末の "~ に置換する処理を一つに纏めることができる。 その場合の置換コマンドは以下のようになる。
's/(^"|"$)/~/g' ・・・ -E オプションで拡張正規表現を使う場合
's/\(^"\|"$\)/~/g' ・・・ GNU sed で基本正規表現を使う場合

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

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 's/","/~,~/g' in.csv | sed 's/^"/~/' | sed 's/"$/~/' | sed 's/\\"/"/g' | sed '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	0m13.180s
4user	0m13.089s
5sys	0m0.089s

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

1$ time sed 's/","/~,~/g' bigIn.csv | sed 's/^"/~/' | sed 's/"$/~/' | sed 's/\\"/"/g' | sed 's/\\\\/\\/g' > /dev/null
2
3real	0m6.124s
4user	0m20.152s
5sys	0m0.456s

パイプを使った方が少し速い。
パイプを使うと各コマンドはサブシェル実行、すなわちパラレル処理されるので、それが効いたものと思われる。
なお、timeコマンドの表示内容の確認はUNIX/Linuxの部屋 timeコマンドの使い方を参考にした。
このサイトに以下の記述がある。

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

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

関連ページ