はじめに
正規表現を扱う関数は,実際にクエリを作成していく中で本当に頻繁に登場します。ここでは頻度の高い文字列の操作方法を紹介していきます。
A. 正規表現
正規表現は,思い通りにそれを使えるようになるだけでも相当な経験が必要になりますが,本ハンズオンでは,正規表現自身の説明やレクチャーにフォーカスを当てることはできていません。以下に基本的な正規表現の記法を挙げるにとどめておきます。リンク先は有用な外部サイトです。
文字
構文 | Matches |
---|---|
\t | タブ文字 ('\u0009') |
\n | 改行文字 ('\u000A') |
\r | キャリッジリターン文字 ('\u000D') |
\f | 用紙送り文字 ('\u000C') |
\a | 警告 (ベル) 文字 ('\u0007') |
\e | エスケープ文字 ('\u001B') |
\cx | x に対応する制御文字 |
数量子
1. 最長一致
構文 | Matches |
---|---|
X? | X、1 または 0 回 |
X* | X、0 回以上 |
X+ | X、1 回以上 |
X{n} | X、n 回 |
X{n,} | X、n 回以上 |
X{n,m} | X、n 回以上、m 回以下 |
2. 最短一致数量子
構文 | Matches |
---|---|
X?? | X、1 または 0 回 |
X*? | X、0 回以上 |
X+? | X、1 回以上 |
X{n}? | X、n 回 |
X{n,}? | X、n 回以上 |
X{n,m}? | X、n 回以上、m 回以下 |
3. 強欲な数量子
構文 | Matches |
---|---|
X?+ | X、1 または 0 回 |
X*+ | X、0 回以上 |
X++ | X、1 回以上 |
X{n}+ | X、n 回 |
X{n,}+ | X、n 回以上 |
X{n,m}+ | X、n 回以上、m 回以下 |
論理演算子
構文 | Matches |
---|---|
XY | X の直後に Y |
X | Y |
(X) | X、前方参照を行う正規表現グループ |
文字クラス
1. 基本
構文 | Matches |
---|---|
[abc] | a、b、または c (単純クラス) |
[^abc] | a、b、c 以外の文字 (否定) |
[a-zA-Z] | a 〜 z または A 〜 Z (範囲) |
[a-d[m-p]] | a 〜 d、または m 〜 p:[a-dm-p] 結合 |
[a-z&&[def]] | d、e、f 交差 |
[a-z&&[^bc]] | b と c を除く a 〜 z:[ad-z] 減算 |
[a-z&&[^m-p]] | m 〜 p を除く a 〜 z:[a-lq-z] 減算 |
2. 定義済みの文字クラス
構文 | Matches |
---|---|
. | 任意の文字 (行末記号とマッチする場合もある) |
\d | 数字: [0-9] |
\D | 数字以外: [^0-9] |
\s | 空白文字:[ \t\n\x0B\f\r] |
\S | 非空白文字:[^\s] |
\w | 単語構成文字:[a-zA-Z_0-9] |
\W | 非単語文字:[^\w] |
例
意味 | 例 | 正規表現 |
---|---|---|
半角数値のみで構成されている、もしくは空白 | 123456789 | ^[0-9]*$ |
英字小文字のみで構成されている、もしくは空白 | abcdefg | ^[a-z]*$ |
英字大文字のみで構成されている、もしくは空白 | ABCDEFG | ^[A-Z]*$ |
英字小文字大文字のみで構成されている、もしくは空白 | ABCdefg | ^[a-zA-Z]*$ |
英数字のみで構成されている、もしくは空白 | 12aaAA | ^[0-9a-zA-Z]*$ |
郵便番号 | 123-1234 | ^[0-9]{3}[-][0-9]{4}$ |
日付yyyy/M/d形式※ | 2009/7/29 | ^[0-9]{4}/[01]?[0-9]/[0123]?[0-9]$ |
半角のみで構成されている、もしくは空白 | abc123アイウ#! | ^[-~。-゚]*$ |
全角のみで構成されている、もしくは空白 | あー漢字 | ^[^-~。-゚]*$ |
全角ひらがな か音引き(ー)のみで構成されている、もしくは空白 | とぅーどぅ | ^[ぁ-んー]*$ |
全角カタカナか音引き(ー)のみで構成されている、もしくは空白 | トゥードゥ | ^[ァ-ヶー]*$ |
半角カタカナのみで構成されている、もしくは空白 | ヌルポインター | ^[ヲ-゚]*$ |
境界正規表現エンジン
構文 | Matches |
---|---|
^ | 行の先頭 |
$ | 行の末尾 |
\b | 単語境界 |
\B | 非単語境界 |
\A | 入力の先頭 |
\G | 前回のマッチの末尾 |
\Z | 最後の行末記号がある場合は、それを除く入力の末尾 |
\z | 入力の末尾 |
A-1. regexp_extract(パターンマッチ)
解説
- regexp_extract は正規表現によって記述されたパターンにマッチするか否か,マッチすればそのマッチストリングを返す関数です。
- regexp_extract(string, pattern) は,pattern にパターン文字列を入れます。返値は,1番初めにマッチした部分文字列を返します。
- regexp_extract はマッチすれば初めのマッチストリングの値を返し,マッチしなければ NULL を返します。
- 引数の1つ多い regexp_extract(string, pattern, group) の場合は,pattern にパターン文字列を,group に何番目のグループのマッチストリングを返すかの数値(1から始まる)を入れます。
- 引数の1つ多い regexp_extract(string, pattern, group) の場合は,1つのグループごとに必ず () で囲みます。pattern の中で () を1つも使っていない場合は 0Groups エラーが出ます。
例1. 特定の文字列を含み,任意のグループを取り出す
- URL が,'fluentd.org' を含むかどうかを見てみましょう。
- regexp_extract(string, pattern) を使います。
例2. 特定の文字列を含み,任意のグループを取り出す(誤)
- URL が,'docs.fluentd.org' を含むかどうかを見てみましょう。
- regexp_extract(string, pattern, group) を使います。
- これにマッチした初めのマッチストリングを返すように記述します。(replace=1)
- パターンに () が入っていないため,0Groups エラー(マッチさせられない)が出ています。
例2'. 特定の文字列を含み,任意のグループを取り出す(正)
- pattern を,'docs.fluentd.org' から '(docs.fluentd.org)' に替えます。
- (docs.fluentd.org) が1グループとなり,このグループにマッチした初めのストリングを返すように記述します。
例2''. 特定の文字列を含む(WHERE)(誤)
- (docs.fluentd.org) にマッチするレコードのみを抽出します。
- WHERE 句では真偽値を返す記述しか書けないため,以下のクエリは間違いです(∵文字列を返しているため)
例2''. 特定の文字列を含む(WHERE)(正)
- (docs.fluentd.org) にマッチするレコードのみを抽出します。
- 「WHERE regexp_extract(td_url,'docs.fluentd.org') IS NOT NULL」
- 「WHERE regexp_extract(td_url,'(docs.fluentd.org)',1) IS NOT NULL」
- の記述が正解です。これなら TRUE/FALSE しか返さないので WHERE 句に記述できますね。
例3. 1つもマッチしない場合の返値を確認
- pattern を,'(hoge)' に替えます。
- この pattern にマッチする URL はないので,返値には NULL か '', 0, はたまた -1 か,何が返ってくるかを確認します。
注意点
1. マッチしない場合の regexp_extract の挙動は Presto では NULL を返しますが,Hive では '' を返すので要注意です!
例4. 複数のグループを記述する
- URL が,'docs.fluentd.org' を含み,かつその後で 'out_file' を含むものを抽出します。
- pattern は '(docs.fluentd.org).*(out_file)' と書きます。グループ間の '.*' は「その間に何らかの文字が入る(文字が無くても良い)」の意味で,これが無い場合は連続する場合のみ:「'docs.fluentd.orgout_file'」の文字列しかマッチしないことになります。
- 以下のパターンを試します。
- OK:'https://docs.fluentd.org/v0.12/articles/out_file'
- NG:'https://docs.fluentd.org/v0.12/articles/out_forward' (2番目のグループを含まない)
- NG:'https://www.fluentd.org/v0.12/articles/out_file' (1番目のグループを含まない)
- NG:'out_file/article/docs.fluentd.org/' (グループの出現順序が異なる)
- また,group の値を 1 から 2 に返ると,マッチする場合の返値が変わりますが,マッチしない場合の結果は NULL で変わりません。
例5. いずれかのグループにマッチする
- 例3で以下の pattern を例3で試します:
- '(docs.fluentd.org).*(out_file)'
- '(docs.fluentd.org)(out_file)'
- '(docs.fluentd.org|out_file)'
- '(docs.fluentd.org|out_file)' は,「'docs.fluentd.org' または 'out_file'」の意味になり,どちらか片方にマッチすれば良いことになり,また順番が違っていても問題ありません。両方マッチした場合の返値は,先にマッチした方の値が返ります。(pat10)
- 候補から選択(|) (参考リンク)
- 選択と量指定子の組み合わせ (参考リンク)
- 候補の中に他のメタ文字を記述 (参考リンク)
例5'. いずれかのグループにマッチする
- URL の最後が特定の値のモノだけを抽出します。
- '(out_splunk$|out_file$|out_forward$|out_secure_forward$|out_exec$|out_exec_filter$|out_copy$)' の $ は末端記号です。例えば out_splunk$ は 'hoge/out_splunk' にはマッチしますが 'out_splunk/hoge' にはマッチしません。
- 上記のパターンは '(out_.*$)' と非常に簡単に記述できます。
例6. マッチした文字列を全て取り出す
- regexp_extract_all(string, pattern) は,マッチした文字列を array の返値として全て取り出します。
A-1'. regexp_like(パターンマッチ)
解説
- regexp_like は正規表現によって記述されたパターンにマッチするか否か,マッチすれば TRUE を,マッチしなければ FALSE を返す関数です。
例1. 特定の文字列を含むか否か
例2. 特定の文字列を含む(WHERE)
- regexp_like を WHERE 句に使う場合は,返値が元々真偽値なので,「IS NOT NULL」の様な付け足しは不要(間違い)です。
A-2. regexp_replace(置換)
解説
- regexp_replace は正規表現によって記述されたパターンにマッチした部分を別の文字列で置換します。
- マッチしなかった場合には置換されず,元の文字列が返ります。
例1'. 文字列の除去
- 特定の文字列を除去する最も簡単なパターンです
例1'. 複数のパターンを列挙して一度に除去(URL の整形)
- 複数のパターンを | で列挙し,一度に除去することができます。(replace4)
例2
- 置換する文字列を指定します。複数マッチする場合には全ての箇所がその置換文字列に換わります。
例2'
- 置換する文字列を,置換後の文字列に再利用したい場合は $N(Nはグループ番号)を使います。
- グループの概念を使うので,パターンは () で囲みます。
例2''
- 複数のグループをパターンに出した例を考えます。
- 'http://' と 'docs.fluentd.org' を入れ替えてみましょう。
例3
- 'http://' を '_g1_'に, 'docs.fluentd.org' を '_g2_' に同時に置換します。
A-3. regexp_split(正規表現による分割)
解説
- regexp_split は正規表現のパターンによって文字列を分割します。
コメント
0件のコメント
ログインしてコメントを残してください。