独習Python [ 山田 祥寛 ]
こんにちは、monachan_papa です。
今回は Python の標準モジュールである reモジュールを使った正規表現について、解説します。
そもそも正規表現はプログラミング一般において、文字列処理を大変便利にしてくれる高度な機能です。
さらに、Python だけの機能ではないので、他のプログラミング言語でも利用されています。正規表現の書き方の違いがあることもありますが、基本は同じと考えて良いです。
よって、一度考え方やベースをマスターすれば、今後においても何かとお得です。
しかしながら、正規表現は書き慣れていないと、扱いが難しいという面があります。
おまけに正規表現自体、奥が深すぎるので例えば書店に行くと、正規表現だけの分厚い参考書が置かれていたりするくらいです。
よって、今回は正規表現のメタ文字(特殊文字)にフォーカスを絞って、解説をしていきます。
本テーマを読み自分で実際に書いてみることで、正規表現のメタ文字の基礎がきっちりとできあがります。
正規表現の書き方や基本用語など
まずは、正規表現はどうやって書くのか、という根幹や前提知識について触れます。
正規表現は通常の文字列(リテラル)と特別な意味を持った記号(メタ文字)を組み合わせることによる文字列表現です。
そして、リテラルとメタ文字の組み合わせで作った条件のことを、パターンと呼びます。
つまり、条件である「パターン」を書くのが正規表現なのです。
リテラル | メタ文字 | パターン |
---|---|---|
通常の文字列 | 特別な意味を持った記号 | リテラルとメタ文字の組み合わせ |
さて、正規表現を使えば、Python の文字列メソッドだけでは実現できない高度な文字列処理が可能にはなります。しかし、ここで文字列処理を行う基本スタンスをあらかじめ提示しておきます。
- まずは、Python の文字列メソッドで実現可能か検討する。
- 不可能な場合、reモジュールを使った正規表現を検討する。
以上を推奨します。正規表現は高度な処理ができる反面、書き方に考慮漏れがあると思わぬ結果をもたらすことがあるからです。
文字列メソッドだけでいけるなら、文字列メソッドを使いましょう。
代表的なメタ文字
下表は、代表的なメタ文字の一覧です。
以降でこれらについて、すべてコード例を示しながら解説していきます。
メタ文字 | 意味 |
---|---|
. | 改行以外の任意の1文字 |
^ | 文字列の先頭 |
$ | 文字列の末尾 |
* | 直前の文字列が0以上繰り返し |
+ | 直前の文字列が1以上繰り返し |
? | 直前の文字列が0回もしくは1回繰り返し |
| | いずれか |
(……) | 括弧内をグルーピングする |
\ | 直後のメタ文字をエスケープ(無効化) |
[……] | 角括弧内のいずれかの1文字 |
[^……] | 角括弧内のいずれでもない |
{n} | 直前の文字列の繰り返し数 |
{n,} | 直前の文字列の最小繰り返し数 |
{n,m} | 直前の文字列の繰り返し数の範囲 |
まずは1つだけ関数を覚えましょう!
正規表現で文字列処理をするために専用の関数を使いますので、reモジュールをインポートします。
import re
関数は色々ありますが、今回は findall関数 という関数だけで良いので覚えましょう。findall関数は、パターンに一致するものをすべてリストで返します。
その他の関数については、必要に応じて使いながら覚えていけば良いです。
パターン記述例
パターン記述例です。内容によって、パターンに一致する例と、マッチしない例を載せてあります。
なぜそうなるのかを考えながら、理解していってください。
ただし、一度にすべて理解する必要はなく、また使いこなす必要もありません。少しずつ自分のものにしていけば良いです。
メタ文字 | 意味 |
---|---|
. | 改行以外の任意の1文字 |
# aと任意の2文字
text = 'abc'
result = re.findall(r'a..', text)
print(result)
['abc']
メタ文字 | 意味 |
---|---|
^ | 文字列の先頭 |
# 文字列の先頭がab
text = 'abc'
result = re.findall(r'^ab', text)
print(result)
text = 'xyzabc'
result = re.findall(r'^ab', text)
print(result)
['ab']
[]
メタ文字 | 意味 |
---|---|
$ | 文字列の末尾 |
# 文字列の末尾がbc
text = 'abc'
result = re.findall(r'bc$', text)
print(result)
text = 'abcxyz'
result = re.findall(r'bc$', text)
print(result)
['bc']
[]
メタ文字 | 意味 |
---|---|
* | 直前の文字列が0以上繰り返し |
# 直前の文字列であるabが0以上繰り返し
text = 'abc'
result = re.findall(r'ab*c', text)
print(result)
text = 'ac'
result = re.findall(r'ab*c', text)
print(result)
['abc']
['ac']
メタ文字 | 意味 |
---|---|
+ | 直前の文字列が1以上繰り返し |
# 直前の文字列であるabが1以上繰り返し
text = 'ab'
result = re.findall(r'ab+', text)
print(result)
text = 'a'
result = re.findall(r'ab+', text)
print(result)
['ab']
[]
メタ文字 | 意味 |
---|---|
? | 直前の文字列が0回もしくは1回繰り返し |
# 直前の文字列であるabが0回もしくは1回繰り返し
text = 'a'
result = re.findall(r'ab?', text)
print(result)
text = 'ab'
result = re.findall(r'ab?', text)
print(result)
text = 'abb'
result = re.findall(r'ab?', text)
print(result)
['a']
['ab']
['ab']
メタ文字 | 意味 |
---|---|
| | いずれか |
# abかcdのいずれか
text = 'ab'
result = re.findall(r'ab|cd', text)
print(result)
text = 'cd'
result = re.findall(r'ab|cd', text)
print(result)
text = 'abcd'
result = re.findall(r'ab|cd', text)
print(result)
text = 'xy'
result = re.findall(r'ab|cd', text)
print(result)
['ab']
['cd']
['ab', 'cd']
[]
メタ文字 | 意味 |
---|---|
(……) | 括弧内をグルーピングする |
# xから始まり、abかcdに一致
text = 'xcd'
result = re.findall(r'x(ab|cd)', text)
print(result)
text = 'xabxy'
result = re.findall(r'x(ab|cd)', text)
print(result)
text = 'abcd'
result = re.findall(r'x(ab|cd)', text)
print(result)
text = 'xa'
result = re.findall(r'x(ab|cd)', text)
print(result)
['cd']
['ab']
[]
[]
メタ文字 | 意味 |
---|---|
\ | 直後のメタ文字をエスケープ(無効化) |
# 直後のメタ文字をエスケープ(無効化)
text = 'ab?'
result = re.findall(r'\?', text)
print(result)
['?']
メタ文字 | 意味 |
---|---|
[……] | 角括弧内のいずれかの1文字 |
# 0から9の数字のいずれか1文字
text = '1'
result = re.findall(r'[0-9]', text)
print(result)
text = 'a'
result = re.findall(r'[0-9]', text)
print(result)
# aからzまでのアルファベットのいずれか1文字
text = 'x'
result = re.findall(r'[a-z]', text)
print(result)
text = '#'
result = re.findall(r'[a-z]', text)
print(result)
['1']
[]
['x']
[]
メタ文字 | 意味 |
---|---|
[^……] | 角括弧内のいずれでもない |
# 0から9の数字のいずれか1文字でない
text = '1'
result = re.findall(r'[^0-9]', text)
print(result)
text = 'a'
result = re.findall(r'[^0-9]', text)
print(result)
# aからzまでのアルファベットのいずれか1文字でない
text = 'x'
result = re.findall(r'[^a-z]', text)
print(result)
text = '#'
result = re.findall(r'^[^a-z]', text)
print(result)
[]
['a']
[]
['#']
メタ文字 | 意味 |
---|---|
{n} | 直前の文字列の繰り返し数 |
# 直前の文字列であるaが3回繰り返し
text = 'aaa'
result = re.findall(r'a{3}', text)
print(result)
text = 'aaaa'
result = re.findall(r'a{3}', text)
print(result)
['aaa']
['aaa']
メタ文字 | 意味 |
---|---|
{n,} | 直前の文字列の最小繰り返し数 |
# 直前の文字列であるaが最小で3回繰り返し
text = 'a'
result = re.findall(r'a{3,}', text)
print(result)
text = 'aaa'
result = re.findall(r'a{3,}', text)
print(result)
text = 'aaaa'
result = re.findall(r'a{3,}', text)
print(result)
[]
['aaa']
['aaaa']
メタ文字 | 意味 |
---|---|
{n,m} | 直前の文字列の繰り返し数の範囲 |
# 直前の文字列であるaが3回から5回まで繰り返し
text = 'a'
result = re.findall(r'a{3,5}', text)
print(result)
text = 'aaa'
result = re.findall(r'a{3,5}', text)
print(result)
text = 'aaaaaa'
result = re.findall(r'a{3,5}', text)
print(result)
[]
['aaa']
['aaaaa']
貪欲マッチと非貪欲マッチ
Python の正規表現には、実は特殊な働きがあります。
デフォルトでは、貪欲マッチと呼ばれる「できるだけ長い文字列一致」をするように抽出されます。
しかし、末尾に「?」をつけると、非貪欲マッチと呼ばれる「可能な限り少ない文字列一致」をするように抽出されます。
記法 | 分類 | 作用 |
---|---|---|
正規表現 | 貪欲マッチ | できるだけ長い文字列一致 |
正規表現? | 非貪欲マッチ | 可能な限り少ない文字列一致 |
貪欲マッチ(最長一致)
以下は、貪欲マッチのコード例です。
i から最後のスペースまで抽出されていることが分かります。
# 貪欲マッチ
text = 'Now is better than never.'
result = re.findall(r'i.*\s', text)
print(result)
['is better than ']
非貪欲マッチ(最短一致)
以下は、非貪欲マッチのコード例です。
? をつけたことにより、i から直近のスペースまでしか抽出されていないことが分かります。
# 非貪欲マッチ
text = 'Now is better than never.'
result = re.findall(r'i.*?\s', text)
print(result)
['is ']
以上で、Python の正規表現とメタ文字についての解説を終わります。
正規表現は、とにかく書くことによる慣れが必要です。
人によって、あるいはシーンによって、よく使う正規表現とそうでない正規表現もあると思います。
もし、書き方に戸惑うときは、自分のスキルにする絶好のチャンスです。
そんな風に少しずつ、正規表現スキルを高めていただけたら、と思います。
reモジュールのもっと色々な関数や、正規表現についてもっと知りたい御方は独習Python [ 山田 祥寛 ]が大変おすすめです。
正規表現だけで20ページ近くに渡り、基礎からから応用までバッチリ学ぶことができます。
正規表現は、吟味に吟味を重ねた結果、難しい文字列表現ができたときは感動します。
是非、頑張ってください。
独習Python [ 山田 祥寛 ]
また、爆速で 正規表現 を学びたい御方にはプログラミングスクールを考えるのも、ひとつの手です。独学よりも効果が出やすいですが、いかんせん投資がけっこうかかります。しかし、techgymというスクールは通うか通わないかは別として、無料のサンプルテキスト&解説動画がもらえます。これをとりあえず getしてまずは試しに体験学習するのもありでしょう。
コメント