CSVでカンマセパレータだけを置換する(フィールド内カンマはスルー)

先の記事で、ExcelVBAによるCSVインポートの自作機能を紹介した。その時に端折っていた正規表現によるカンマセパレータの置換を説明する。これが理解できれば、フィールド内の改行コードと行端の改行コードが同じ場合でも対処できる。

また、サクラエディタなどのテキストエディタで、予めCSVデータを今回紹介する正規表現を使って置換しておけば、Excel標準のインポートでもレイアウトを崩さずに取り込めるだろう。

カンマセパレータを置換するための正規表現

それでは本題。カンマのケースを見ていく。次のCSVデータがあるとする。
 a,"b,c",d,"e,f,g",h

目標は、カンマセパレータを別文字(ここでは@)に置換することだ。
 a@"b,c"@d@"e,f,g"@h

先に結論を示すが、フィールド内カンマを無視して、セパレータとしてのカンマだけにマッチする正規表現が以下だ。
 ,(?=([^"]*"[^"]*")*(?![^"]*"))

これにマッチしたカンマを別文字に置換すれば良い。

パターンを見出す

このパターンの意図について、説明を試みる。

図の一番上のように、今回扱うCSVデータには、4つのカンマセパレータがある。便宜上、1番目から4番目のカンマセパレータのそれぞれについて、後続の文字列に下線を引いている。

この下線部分にパターンを見いだせる。ダブルクォートがある場合、単独ではなくペアになっていることがわかる。また、フィールド内のカンマの場合は、ダブルクォートのペアを作ろうとすると最後に必ず1個余ることになり、上述のパターンにマッチしない。

さらにこのパターンを正確に表現すれば、次のようになる。

カンマの後続が次のパターンにマッチすれば、そのカンマはセパレータである。
①「"」を含まない文字列②「"」括られた文字列③行末まで「"」を含まない文字列

  1. ③は、フィールド内カンマをアンマッチにするための条件になる。ダブルクォートが最後に1個余るパターンを除外する。
  2. ①+②のグループは繰り返し出現しても良い。

そして既に示した正規表現は、このパターンそのものである。

図のオレンジ色が①、緑色が②、紫色が③に相当する。
緑色の両端にダブルクォートが付いていることに留意すること。ダブルクォートのペアだ。

正規表現のおさらい

遅まきながら、この正規表現を理解するためのおさらいをしておく。

正規表現内容
[^a]a以外の文字にマッチ
*直前の文字が 0回以上 繰り返す場合にマッチ
(AB)*パターンA, パターンBを一つのグループとして、そのグループが 0回以上 繰り返す場合にマッチ
ptn1(?=ptn2)先読み肯定グループ。直後に ptn2 がある ptn1 にマッチ
ptn1(?!ptn2)先読み否定グループ。直後に ptn2 がない ptn1 にマッチ

先読み肯定グループ、先読み否定グループについては、以下サイトが詳しい。
https://abicky.net/2010/05/30/135112/

正規表現の解析

1番目のセパレータについて、本当にパターンにマッチしているか確認してみる。

正規表現
実際のデータ

オレンジ色と緑色のグループが2回出現していることになる。 正規表現のオレンジ色の最後にアスタリスク「*」が付いているので、オレンジ色がなく緑色の「”b,c”」だけでも、正規表現の黄色のグループとしてマッチする。

また、正規表現の黄色の最後にもアスタリスク「*」が付いているので、グループの繰り返しの出現にマッチする。(「,d,」「"e,f,g"」)は、2回目のグループ出現としてカウントされる。

2~4番目のセパレータについても同様の考え方で、上記の正規表現にマッチすることが分かる。

正規表現の動作チェック

正規表現の動作チェックをWeb上で行える。
例えば、https://regexr.com/ とか、https://weblabo.oscasierra.net/tools/regex/ など。
当記事の正規表現と対象テキストを設定してみた結果が下図。

マッチした部分に自動で色がつくので分かりやすい。
いろいろ試すと勉強になる。
試しにグループの繰り返しを指定する「*」を「{2}」に変えてみた結果が下図。(「{2}」は直前パターンの2回の繰り返しにマッチする)

1番目のセパレータがマッチした。このセパレータの後ろには、ダブルクォートのペアが2個あるのでマッチしたのだ。
次に「{1}」に変えてみた。

2番目と3番目のセパレータがマッチした。このセパレータの後ろには、ダブルクォートのペアが1個だけなのでマッチしたのだ。
最後に「{0}」に変えてみる。

4番目のセパレータがマッチした。このセパレータの後ろには、ダブルクォートのペアが無いのでマッチしたのだ。

正規表現による置換の例

ここまでくれば、あとはセパレータを別文字に置換するだけだ。
先の記事で自作CSVインポート機能のVBAコードを示したが、その中のrepファンクションは、上記の正規表現を使ってカンマセパレータを別文字に置換している。
サクラエディタなら以下のような感じで。

おわりに

今回の正規表現では先読み(look ahead)・後読み(look behind)が使用されているが、sed コマンドではそれが使用できない。
その場合の対応方法は、sed 特有の制御方法があるので、「CSVでカンマセパレータだけを置換する(フィールド内カンマはスルー)・・・SED版」に記す。