Database, "postgres, mysql, sqlite", Programing, "perl, php, python, ruby"

Contents
- ドキュメントの正規表現の箇所、関連リンク、実行環境
- 肯定先読みで、CSV
中の「列区切りでないカンマ」を検索 - 否定先読みで「列区切りカンマ」だけを検索
ドキュメントの正規表現の箇所、関連リンク、実行環境
- PostgreSQL 9.4.5
文書 - 文字列関数と演算子 - 〃 パターンマッチ
- 正規表現マスター 肯定先読み~否定後読み
- pgpcre(PostgreSQL
に Perl 互換の正規表現 PCRE を追加する拡張)
PostgreSQL
分かりやすい説明は、例えば上に挙げた正規表現マスターを見て下さい。ここでは肯定/否定
実行環境は
肯定先読みで、CSV 中の「列区切りでないカンマ」を検索
今回、見やすさのためサンプル文字列の各カンマの後に半角空白を一つ付けており、以下ではそれを含めて「カンマ」と書き、処理対象にします。また二重引用符は「一部の列を囲む」だけに使われ、データ内には無い前提。本来の目的は「列区切りのカンマだけ」を正確に取り出すことですが、「列区切りでない」方が二重引用符に囲まれたデータ内にあるので、むしろ捕捉しやすそう。そっちを先にカンマ以外(例えば
まず準備体操。冒頭のサンプル文字列を対象として、すべてのカンマを正規表現関数
-- すべてのカンマを改行に置換 select regexp_replace( 'Database, "postgres, mysql, sqlite", Programing, "perl, php, python, ruby"', ', ', E'\n', 'g'); +----------------+ | regexp_replace | +----------------+ | Database +| | "postgres +| | mysql +| | sqlite" +| | Programing +| | "perl +| | php +| | python +| | ruby" | +----------------+ (1 row)
当然ながら「列区切りカンマ」と「データ内カンマ」が等しく置換されます。次に「データ内カンマ」だけ
-- データ内カンマだけ置換したいが、うまく行かない例 select regexp_replace( 'Database, "postgres, mysql, sqlite", Programing, "perl, php, python, ruby"', '("[^"]*), ([^"]*")', '\1<br>\2', 'g'); +--------------------------------------------------------------------------------+ | regexp_replace | +--------------------------------------------------------------------------------+ | Database, "postgres, mysql<br> sqlite", Programing, "perl, php, python<br> ruby" | +--------------------------------------------------------------------------------+ (1 row)
そこで「肯定先読み」を使い、
-- 肯定先読みの例、ただし最後の列だけ未処理 select regexp_replace( 'Database, "postgres, mysql, sqlite", Programing, "perl, php, python, ruby"', ', (?=[^"]*",)', '<br>', 'g'); +--------------------------------------------------------------------------------+ | regexp_replace | +--------------------------------------------------------------------------------+ | Database, "postgres<br> mysql<br> sqlite", Programing, "perl, php, python, ruby" | +--------------------------------------------------------------------------------+ (1 row)
未処理で残った「最終列内のカンマ」は、後ろが「二重引用符以外の文字列+二重引用符+行末」となるものを全て捕捉すれば
-- 肯定先読みで「列区切りでないカンマ」を全て置換する例 select regexp_replace( 'Database, "postgres, mysql, sqlite", Programing, "perl, php, python, ruby"', ', (?=[^"]*(",|"$))',-- 初出時は不要なカッコがあったので削除. 以下同様(1月21日) '<br>', 'g'); +--------------------------------------------------------------------------------------+ | regexp_replace | +--------------------------------------------------------------------------------------+ | Database, "postgres<br> mysql<br> sqlite", Programing, "perl<br> php<br> python<br> ruby" | +--------------------------------------------------------------------------------------+ (1 row)

否定先読みで「列区切りカンマ」だけを検索
前項で「列区切りでないカンマ」を全て置換できたので、後は全てのカンマ=列区切り。もう正規表現は必要なく一括置換すれば済みますが、もし先に「列区切りカンマ」だけ検索select regexp_replace( 'Database, "postgres, mysql, sqlite", Programing, "perl, php, python, ruby"', ', (?![^"]*(",|"$))',-- 先頭を ?= から ?! に変更しただけ E'\n', 'g'); +---------------------------+ | regexp_replace | +---------------------------+ | Database +| | "postgres, mysql, sqlite"+| | Programing +| | "perl, php, python, ruby" | +---------------------------+ (1 row)

↓ 全体をまとめたクエリ。WITH
with a (str) as ( values('Database, "postgres, mysql, sqlite", Programing, "perl, php, python, ruby"') ) select 0 as id, str from a-- 元の文字列 union all select 1, regexp_replace(str, ', (?=[^"]*(",|"$))', '<br>', 'g') from a-- 肯定先読み union all select 2, regexp_replace(str, ', (?![^"]*(",|"$))', E'\n', 'g') from a;-- 否定先読み +----+--------------------------------------------------------------------------------------+ | id | str | +----+--------------------------------------------------------------------------------------+ | 0 | Database, "postgres, mysql, sqlite", Programing, "perl, php, python, ruby" | | 1 | Database, "postgres<br> mysql<br> sqlite", Programing, "perl<br> php<br> python<br> ruby" | | 2 | Database +| | | "postgres, mysql, sqlite" +| | | Programing +| | | "perl, php, python, ruby" | +----+--------------------------------------------------------------------------------------+ (3 rows)

↓ 確認のため、最後だけでなく最初の列もカンマを含む場合。同じ正規表現で対応できてます。
with a (str) as ( values('"postgres, mysql, sqlite", Database, Programing, "perl, php, python, ruby"') ) select 0 as id, str from a-- 元の文字列 union all select 1, regexp_replace(str, ', (?=[^"]*(",|"$))', '<br>', 'g') from a union all select 2, regexp_replace(str, ', (?![^"]*(",|"$))', E'\n', 'g') from a; +----+--------------------------------------------------------------------------------------+ | id | str | +----+--------------------------------------------------------------------------------------+ | 0 | "postgres, mysql, sqlite", Database, Programing, "perl, php, python, ruby" | | 1 | "postgres<br> mysql<br> sqlite", Database, Programing, "perl<br> php<br> python<br> ruby" | | 2 | "postgres, mysql, sqlite" +| | | Database +| | | Programing +| | | "perl, php, python, ruby" | +----+--------------------------------------------------------------------------------------+ (3 rows)

「先読み」「後読み」と何回も書いてると、TUKUYOMI