概要 | |
---|---|
目的 | 正規表現の構文の基礎を理解する |
Goal | 文字列のキャプチャと置換ができるようになる |
参考 | Python 3.9 正規表現 正規表現技術入門 ――最新エンジン実装と理論的背景 |
key word | 正規表現, regular expression |
1. 正規表現の基本
正規表現とは
正規表現(regular expression)とは文字列のパターンを記述するための「表現式」です。表現式ということなので数式のように書き方に一定の構文規則(syntax)があります。式は以下の要素の規則的な組み合わせで表現されます:
要素 | 説明 |
---|---|
オペランド | 構成要素。例:数式における数 |
オペレーター | 演算子。例:数式における+ や- |
一例として、
1
0|1|2|3|4|5|6|7|8|9
という正規表現は、0から9までのいずれかの数字1文字という文字列のパターンを表現しています。このパターンは[0-9]
や\d
という正規表現でも表現できますが、詳細は後述します。なお「正規」とはある規則に則ってるという意味で理解するとわかりやすいです。
正規表現はどのような時に使うのか?
コンピューターのプログラムやデータの多くは文字列で表現されています。そのデータやプログラムを対象に
- 文字列検索
- 文字列の書き換え
- 入力形式のvalidation
といった操作をする時に正規表現は役に立ちます。例えば、とある入力フォームで携帯電話番号を申請者に入力してもらうボックスがあったとします。この時、080-6666-7777
といった形式で入力してもらいたいが入力情報がそのフォーマットに則っているか確かめたい場合、
1
\d{3}(-\d{4}){2}
という正規表現にマッチする文字列しか認めない関数を組むとaaa-rtyu-gbnu
や080-99-99
といった入力を拒絶することができます。また、Pythonなどのプログラミング言語の変数名のvalidation処理(例:変数名はアルファベットか_で始まらなくてはならない)も、正規表現で実現されています。そのほかにも多くのテキストエディターで実装されているシンタックスハイライトも、正規表現で特定のプログラミング言語に応じた文字列規則の抽出という処理を実施しています。
メタ文字とリテラル
正規表現において文字はメタ文字とリテラルの二つの種類に分類されます。
- メタ文字 : metacharacter, 特別な意味を文字のこと
- リテラル : literal, 特別な意味を持たない文字のこと
メタ文字の分類
メタ文字は量指定子やアンカーなどの種類に分類されます。
種類 | 説明 |
---|---|
量指定子 | 繰り返しに関する演算子 |
アンカー(位置指定子) | 位置を表す演算子 |
エスケープシーケンス | 事前に定義されたオペランド |
文字クラス | 文字のセットを定義する演算子 |
ドット | 任意の1文字 |
量指定子
説明 | 正規表現の例 | マッチする例 | |
---|---|---|---|
+ |
直前の文字が 1回以上 繰り返す場合にマッチします。最長一致。条件に合う最長の部分に一致します。 | go+gle |
gogle,go…gle |
* |
直前の文字が 0回以上 繰り返す場合にマッチします。最長一致。条件に合う最長の部分に一致します。 | go*gle |
gglego…gle |
? |
直前の文字が 0個か1個 の場合にマッチします。最長一致。条件に合う最長の部分に一致します。 | go?gle |
ggle,gogle |
+? |
直前の文字が 1回以上 繰り返す場合にマッチします。最短一致。条件に合う最短の部分に一致します。 | go+?gle |
gogle,go…gle |
*? |
直前の文字が 0回以上 繰り返す場合にマッチします。最短一致。条件に合う最短の部分に一致します。 | go*?xxxgle |
ggle,go…gle |
?? |
直前の文字が 0個か1個 の場合にマッチします。最短一致。条件に合う最短の部分に一致します。 | go??gle |
ggle, gogle |
{n} |
直前の文字の桁数を指定できます。 | a{3} |
aaa |
{n,} |
直前の文字の最小桁数のみ指定できます。 | a{3,} |
aaa, aaaa… |
{n,m} |
直前の文字の最小桁数と最大桁数を指定できます。最長一致。条件に合う最長の部分に一致します。 | a{3,4} |
aaa, aaaa |
{n,m}? |
直前の文字の最小桁数と最大桁数を指定できます。最短一致。条件に合う最短の部分に一致します。 | a{3,4}? |
aaa, aaaa |
アンカー(位置指定子)
文字 | 説明 | 正規表現の例 | マッチする例 |
---|---|---|---|
^ |
直後の文字が行の先頭にある場合にマッチします。 | ^google |
google… |
$ |
直前の文字が行の末尾にある場合にマッチします。 | google$ |
|
\< |
単語の先頭にマッチします。 | \< |
|
\> |
単語の末尾にマッチします。 | \> |
google* |
\b |
単語の先頭か末尾にマッチします。 | \b |
|
\B |
単語の先頭か末尾以外にマッチします。 | \B |
|
\A |
ファイルの先頭にマッチします。 | \A |
(なし) |
\z |
ファイルの末尾にマッチします。 | \z |
(なし) |
\G |
直前の一致文字列の末尾にマッチします。 | \G |
(なし) |
エスケープシーケンス
文字 | 説明 | 対応する表現 |
---|---|---|
\t |
タブ | (なし) |
\r |
改行。CR(Carriage Return:0x0D) | (なし) |
\n |
改行。LF(Line Feed:0x0A) | (なし) |
\d |
すべての数字 | [0-9] |
\D |
すべての数字以外の文字 | [^0-9] |
\s |
垂直タブ以外のすべての空白文字 | [ \t\f\r\n] |
\S |
すべての非空白文字 | [^ \t\f\r\n] |
\w |
アルファベット、アンダーバー、数字 | [a-zA-Z_0-9] |
\W |
アルファベット、アンダーバー、数字以外の文字 | [^a-zA-Z_0-9] |
その他
説明 | 正規表現の例 | マッチする例 | |
---|---|---|---|
| |
いずれかの条件 (OR条件) として使われます。 | goog(le|ol) |
google, googol |
\ |
直後の正規表現記号(メタ文字)を エスケープ します。 | go\+gle |
go+gle |
. |
任意の1文字 にマッチします。 | . |
A,あ |
[...] |
角括弧に含まれるいずれか1文字にマッチします。 | [abc] , [a-c] |
a, b, c |
[^...] |
角括弧に含まれる文字以外にマッチします。 | [^abc] , [^a-c] |
a, b, c以外の文字 |
(...) |
文字を1つのグループにまとめることができます。 | goog(le|ol) |
google, googol |
(...) |
優先させたい演算のグループを指定します | (ab){2} |
abab |
2. 正規表現の基本演算:連接, 選択, 繰り返し
正規表現は
- 連接
- 選択
- 繰り返し
という3つの基本演算を備えています。
連接
正規表現を繋げる演算は連接, concatenationと呼ばれます。連接はよく使われる演算なので、演算子として特別な記号はありません。例えばREGEXという文字列はR, E, G,E, Xの5つの文字の連接で成立しています。
選択
r|s
という正規表現はrまたはsというパターンを表しています。
1
[東|京]大
は東大と京大の文字列にマッチします。
繰り返し
\d*
は任意の長さの数字列(空文字も含む)を表したり、[A-Za-z]{1,}
は任意の長さのアルファベット文字列(空文字除く)を表現しています。量指定子を用いることで指定されたパターンの繰り返しから構成される文字列のマッチが可能となります。
演算子の結合順位
演算の結合順位とは、どの演算から先に評価するかという順位規則のことです。正規表現における結合順位は
1
繰り返し > 連結 > 選択
グループ化
数式と同じように丸括弧を使って文字列を括ることでどの演算を優先的に評価するかを明示的に指定することができます。丸括弧を用いて正規表現を括る機能をグループ化と呼びます。
apple|orange*
とするとapple
またはorange*
にマッチするが、(apple|orange)*
ならばappleorange
やorangeorange
などにマッチします。
3. キャプチャと置換
正規表現を用いて文字列のキャプチャと置換を紹介します。文字列に対して正規表現を用いてマッチさせ、マッチした文字列に対して置換という操作をします。
文字列の部位とマッチング
文字列には部位を表す三つの用語があります。prefix, suffix, substringです。
文字列の部位 | 説明 | 例 |
---|---|---|
prefix |
文字列の先頭部分。n文字から構成される文字列のprefixは空文字も含め$n+1$個存在する。 | abは文字列abcdのprefix |
suffix |
文字列の後方部分。n文字から構成される文字列のsuffixは空文字も含め$n+1$個存在する。 | cdは文字列abcdのsuffix |
substring |
文字列に対する部分文字列。n文字から構成される文字列のsubstringは空文字も含め$n\times (n+1)/2 + 1$個存在する。 | bcは文字列abcdのsubstring |
この文字列の部位をマッチングのパターンと関連づけると、
マッチングのパターン | 説明 |
---|---|
前方一致 | 正規表現が与えられた文字列のprefixにマッチ。アンカー, ^ を用いら部分一致と理解できる。 |
後方一致 | 正規表現が与えられた文字列のsuffixにマッチ。アンカー, $ を用いら部分一致と理解できる。 |
部分一致 | 正規表現が与えられた文字列のsubstringにマッチ。Pythonだとreモジュールのsearch() に対応 |
完全一致 | 正規表現が与えられた文字列に完全にマッチ。文字列のvalidationに用いられる。Pythonだとreモジュールのmatch() に対応 |
キャプチャ
正規表現と文字列からサブマッチを取得する処理のことをキャプチャと言います。キャプチャには二つの方法があります。
- 順番で指定する方法
- 名前で指定する方法
の二つです。
順番で指定する場合は、正規表現を用いて抽出されたサブマッチのインデックスを利用して、n番目のサブマッチを取得します。名前で指定する方法は、各サブマッチとその名前を辞書の様に対応させ、各サブマッチを取得する方法です。事例を通してこれらを紹介していきます。
事例 1 順番キャプチャ: bash commandsed
と正規表現を用いてファイルをrenameする
sed
はgrep
と並ぶUnixを代表するコマンドラインツールで、正規表現を使って文字列を置換するときに役に立ちます。s/regex/replacement/g
という構文で正規表現にマッチした文字列を置換します。一例として、
1
2
$ echo 'kirakira bushi' | sed -E 's/([A-Za-z]+) ([A-Za-z]+)/\2 \1/'
bushi kirakira
s/regex/replacement/g
のsは置換(substitute)を意味し、末尾のgはglobalを意味しています。globalとはパターンにマッチする文字列全てを置換するオプションです。
前準備が整ったので以下でash commandsed
と正規表現を用いてファイルのrenameを説明します。
まず現在のworking directoryに
1
$ touch a_20190601.tsv a_20190602.tsv a_20190603.tsv a_20190604.tsv a_20190605.tsv a_20190606.tsv
で空ファイルを作ります。その結果、
1
2
3
4
5
6
a_20190601.tsv
a_20190602.tsv
a_20190603.tsv
a_20190604.tsv
a_20190605.tsv
a_20190606.tsv
というファイルができます。[field1]_[field2].tsv
というファイル名のデータを[field2]_[field1].tsv
に直したいとします。順番で指定する方法でファイルをrenameします。
1
$ ls -1| sed -nr "s/([a-z]+?)_([0-9]+?)\.tsv/mv & \2_\1.tsv/g" | bash
ここでの処理は、
ls -1
でファイル一覧を縦のリストとして取得し、パイプ|
を用いてsed
コマンドにファイル一覧を渡します。sed
の-r
オプションは以下正規表現を使いますよという命令をします。sed
の-n
オプションはマッチしなかったファイルのprint出力をsuppressします/mv
はmvコマンドを使うという意味です。\2
と\1
は()
でキャプチャしたサブマッチのindexを示しています。なお、\0
には、ファイルネームが格納されています。- bashコマンドに渡し、Linuxコマンドとして実行することを意味しています。最後の
bash
は結果の保存のためと理解しといてください。
結果、
1
2
3
4
5
6
a_20190601.tsv
a_20190602.tsv
a_20190603.tsv
a_20190604.tsv
a_20190605.tsv
a_20190606.tsv
となります。
事例 2 名前付きキャプチャ: Pythonと正規表現を用いて日付表現を日本語に直す
次の様なtest.py
ファイルを作成する
1
2
3
4
5
6
7
8
9
import re
def main():
str1 = "2020-11-31"
str2 = re.sub('(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})',r'\g<y>年\g<m>月\g<d>日',str1)
print(str2)
if __name__ == "__main__":
main()
これをpython test.py
で実行すると
1
2
$ python test.py
2020年11月31日
と出力される。(?P<名前>・・・)
と指定すると名前付きキャプチャができるようになる。参照するには\g<名前>
とします。
事例 3 順番キャプチャ: 2015年国勢調査shpファイルをcurlコマンドで一括downloadする
e-Statでは国勢調査のデータを都道府県別で公開している。2015年の国勢調査のデータのPATHはhttps://www.e-stat.go.jp/gis/statmap-search/data?dlserveyId=A002005212015&code=[01-47]&coordSys=1&format=shape&downloadType=5
で[01-47]
が各都道府県の番号に対応している。このファイルをダウンロードし、その名前は都道府県番号と対応させて保存したいとする。
この時、
1
curl 'https://www.e-stat.go.jp/gis/statmap-search/data?dlserveyId=A002005212015&code=[01-47]&coordSys=1&format=shape&downloadType=5' -o '#1.zip'
とすれば#1
の部分にキャプチャした数値が入力され、このコマンドだけで各都道府県のshpファイルをダウンロードすることができる。
(注意:GitHub Accountが必要となります)