「CSV」というと、さまざまなローカライズがあって、前処理を考えると「tab」区切りのほうが扱いやすいです。ただし、注意しなければならないことは、ファイルのフォーマットだけではなく「Excel」のほうにもあります。
コピーペーストでテキストエディタに張り付けると普通にタブ区切りになっていますが、まず、レコード件数(行数)の確認が必要になります。
例えば、ここにExcelで「51,472行」のデータがあります。横方向は67列です。
これを、タブ区切りでテキストエディタに張り付けると「51,583行」になりました。「111行」超過しています。
これを調べるawkのスクリプトは
1 2 3 4 5 6 7 |
BEGIN{ FS="\t"; } { if(NF!=67) print NF "\t" NR; } |
「NF」は、フィールド数を教えてくれる定数です。「NR」は、行数を教えてくれる定数です。
数が少なければ行数をみてテキストエディタで直せばいいのですが、さすがに「111行」もあると手直しは効率的ではありません。
そこで作ったのが下のようなスクリプトです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
BEGIN{ FS="\t"; OFS="\t"; } { if(NF<67){ if(flg==0){# 最初に67フィールドない行 sub(/"/,""); flg=1; buff=$0; }else{# 最初の行ではない sub(/"/,""); buff=buff "●" $0; } if(split(buff,dd,"\t")==67){ flg=0; $0=buff; buff=""; print; } }else{ flg=0; print; } } |
このスクリプトはフィールドが67に満たない行を見つけて、その場合は「buff」と言う変数に代入していきます。
最初に67列ないレコードは変数「buff」にいれて「flg」をセットします。
次に67列ない行は、セル内改行ですからレコードの頭にセル内改行の目印として「●」をつけて「buff」に追加します。awkで文字列の追加は半角スペース開けて記述するだけです。
結合した文字列が67列あると、次のレコードと接続してしまうのを避けるために「split」でチェックしています。同時に「flg」をリセットします。
補足をすると「print」だけの場合は「$0」、つまり本来は読み込んだレコードの出力を意味します。
このスクリプトでは「buff」という変数を使っていますが「$0=buff」としていることには、ここではあまり意味がないのですが「$0」をうまく使うと便利なことも少なからずあるので、いずれ絶好のパターンが来たら説明します。
「OFS」というのは、出力するときのセパレータを指定しています。指定しないとセパレータは半角スペースになってしまいます。
このスクリプトを通すことで、「chk.awk」で67列ないとされたレコードが0件になったので、あとは手作業で確認します。
データを加工したり、集計したりする場合の確認作業は絶対に必要です。ここを怠ると、すべてに対する信頼性にいちゃもんが付くことがありますので、可能な限り納得できるチェックをする必要があります。
51,472レコードのCSVファイルのセル内改行の修正ですがawkでスクリプトを書くのに要した時間は15分程度です。処理は5万行くらいなら「秒」までかかりません。500万行くらいで4、5秒くらいと思います。
ExcelのVBAで処理を自動化するという考えもありますが、実利は圧倒的に「awk」に軍配を上げることができます。
VBAは下位互換も含めた言語仕様になっていてうんざりするほど冗長なうえに、Excelというアプリケーションのコントロールを覚えなければならないので、ちょっとした集計などをするなら瞬殺の「awk」が圧倒的にパフォーマンスが上であると思います。