【即戦力】Python正規表現の基礎から応用まで解説!

はじめに

Python正規表現は、文字列のパターンマッチングを行うための強力なツールです。本記事では、Python正規表現の基礎から応用までを解説します。正規表現でよく使うパターンをチートシートでまとめたので使い方をさくっと確認したい場合はチートシートを見てください。

正規表現の基礎

正規表現は、文字列のパターンマッチングを行うための特殊な文字列です。以下は、正規表現の基本的な構成要素です。

文字クラス

文字クラスは、マッチングする文字を指定するために使用されます。例えば、[abc]という文字列は、a、b、またはcのいずれかとマッチします。

メタ文字

メタ文字は、正規表現の中で特別な意味を持つ文字です。例えば、”.”は任意の1文字とマッチします。

正規表現オブジェクトの使い方

Pythonで正規表現を扱うには、reモジュールを使用します。re.compile()関数を使用して、正規表現パターンから正規表現オブジェクトを作成します。

re.search()

re.search()関数を使用して、文字列内で正規表現にマッチする最初の箇所を検索することができます。

import re

pattern = re.compile(r'foo')
result = pattern.search('seafood')

print(result) # <re.Match object; span=(3, 6), match='foo'>

re.match()

re.match()関数を使用して、文字列の先頭が正規表現にマッチするかどうかを調べることができます。

import re

pattern = re.compile(r'foo')
result = pattern.match('seafood')

print(result) # None

re.fullmatch()

re.fullmatch()関数を使用して、文字列全体が正規表現にマッチするかどうかを調べることができます。

import re

pattern = re.compile(r'foo')
result = pattern.fullmatch('seafood')

print(result) # None

マッチングの高度な操作

正規表現を使って、文字列内の複数の箇所にマッチする文字列を取得することができます。

re.findall()

re.findall()関数を使用して、文字列内で正規表現にマッチするすべての箇所を取得することができます。

import re

pattern = re.compile(r'\d+')
result = pattern.findall('1 apple, 2 bananas, and 3 oranges')

print(result) # ['1', '2', '3']

re.finditer()

re.finditer()関数を使用して、文字列内で正規表現にマッチするすべての箇所に対応するマッチオブジェクトを取得することができます。

import re

pattern = re.compile(r'\d+')
matches = pattern.finditer('1 apple, 2 bananas, and 3 oranges')

for match in matches:
    print(match)

# <re.Match object; span=(0, 1), match='1'>
# <re.Match object; span=(8, 9), match='2'>
# <re.Match object; span=(20, 21), match='3'>

re.split()

re.split()関数を使用して、正規表現パターンにマッチした箇所で文字列を分割することができます。

import re

pattern = re.compile(r'\s+')
result = pattern.split('apple  banana  orange')

print(result) # ['apple', 'banana', 'orange']

re.sub()、re.subn()

re.sub()関数を使用して、正規表現パターンにマッチした箇所を指定の文字列に置換することができます。re.subn()関数は、置換した回数も返します。

import re

pattern = re.compile(r'\d+')
result = pattern.sub('x', '1 apple, 2 bananas, and 3 oranges')

print(result) # x apple, x bananas, and x oranges

result, count = pattern.subn('x', '1 apple, 2 bananas, and 3 oranges')

print(result) # x apple, x bananas, and x oranges
print(count) # 3

グルーピング

グルーピングを使用すると、正規表現パターンの一部分をグループ化して、後で使用することができます。

キャプチャグループ

キャプチャグループを使用すると、正規表現パターンの一部分に名前を付けて、後で取り出すことができます。

import re

pattern = re.compile(r'(?P<first>\w+) (?P<last>\w+)')
result = pattern.match('John Smith')

print(result.group('first')) # John
print(result.group('last')) # Smith

正規表現の記述方法

正規表現には、基本的なパターンがいくつかあります。以下にいくつかの例を示します。

文字列の先頭や末尾などの位置にマッチするパターン

^は、文字列の先頭にマッチします。$は、文字列の末尾にマッチします。

import re

pattern = re.compile(r'^Hello')
result = pattern.search('Hello, World!')

print(result) # <re.Match object; span=(0, 5), match='Hello'>

直前の文字を指定回数繰り返す文字列にマッチするパターン

+は、直前の文字が1回以上繰り返されることを表します。{n}は、直前の文字がn回繰り返されることを表します。

import re

pattern = re.compile(r'a+')
result = pattern.search('baaa')

print(result) # <re.Match object; span=(1, 4), match='aaa'>

pattern = re.compile(r'a{2}')
result = pattern.search('baa')

print(result) # <re.Match object; span=(1, 3), match='aa'>

複数の文字列のいずれかにマッチするパターン

|は、複数の文字列のいずれかにマッチすることを表します。

import re

pattern = re.compile(r'apple|banana')
result = pattern.search('I like apples')

print(result) # <re.Match object; span=(7, 12), match='apples'>

先読みと後読みを使ったパターンの記述

(?=…)は、後ろに続く文字列が指定されたパターンにマッチすることを表します。(?<=…)は、前に続く文字列が指定されたパターンにマッチすることを表します。

import re

pattern = re.compile(r'\w+(?=!)')
result = pattern.findall('Hello! My name is John!')

print(result) # ['Hello']

正規表現フラグ

正規表現には、いくつかのフラグを設定することができます。以下にフラグの一覧を示します。

  • re.IGNORECASE:大文字と小文字を区別しない。
  • re.MULTILINE:複数行の文字列に対して、^や$を行ごとにマッチングさせる。
  • re.DOTALL:改行文字を含む全ての文字にマッチングさせる。

Pythonの正規表現でよく使うパターンのチートシート

import re

# .(任意の1文字)にマッチする
re.search(r'a.b', 'aab')  # 'aab'
re.search(r'a.b', 'acb')  # 'acb'
re.search(r'a.b', 'a$b')  # 'a$b'

# ^(行の先頭)にマッチする
re.search(r'^Hello', 'Hello, world!')  # 'Hello'

# $(行の末尾)にマッチする
re.search(r'world$', 'Hello, world!')  # 'world!'

# \b(単語境界)にマッチする
re.search(r'\bhello\b', 'hello, world')  # 'hello'
re.search(r'\bth', 'this is a pen')  # 'th'
re.search(r'is\b', 'this is a pen')  # 'is'

# \B(単語境界以外)にマッチする
re.search(r'\Bfoo\B', 'seafood')  # 'foo'
re.search(r'foo\B', 'seafood')  # 'foo'
re.search(r'\Bfoo', 'seafood')  # 'foo'

# [](文字集合)にマッチする
re.search(r'[abc]', 'def')  # None
re.search(r'[abc]', 'abc')  # 'a'
re.search(r'[a-z]', 'ABCD')  # None
re.search(r'[0-9]', 'abc123xyz')  # '1'
re.search(r'[^a-z]', 'abc123xyz')  # '1'

# |(いずれか)にマッチする
re.search(r'hello|world', 'hello, world!')  # 'hello'
re.search(r'cat|dog', 'I like cats')  # 'cat'

# ()(グルーピング)にマッチする
re.search(r'(hello)+', 'hellohello, world!')  # 'hellohello'
re.search(r'(cat|dog)', 'I like dogs')  # 'dog'

# *(0回以上の繰り返し)にマッチする
re.search(r'ab*', 'a')  # 'a'
re.search(r'ab*', 'ab')  # 'ab'
re.search(r'ab*', 'abc')  # 'ab'
re.search(r'ab*', 'abcd')  # 'a'

# +(1回以上の繰り返し)にマッチする
re.search(r'ab+', 'a')  # None
re.search(r'ab+', 'ab')  # 'ab'
re.search(r'ab+', 'abc')  # 'ab'
re.search(r'ab+', 'abcd')  # 'ab'

# ?(0回または1回の繰り返し)にマッチする
re.search(r'ab?', 'a')  # 'a'
re.search(r'ab?', 'ab')  # 'ab'
re.search(r'ab?', 'abc')  # 'ab'
re.search(r'ab?', 'abcd')  # 'a'

# {n}(n回の繰り返し)にマッチする
re.search(r'a{3}', 'aaaaa')  # 'aaa'
re.search(r'a{3}', 'aa')  # None

# {m,n}(m回以上、n回以下の繰り返し)にマッチする
re.search(r'a{2,4}', 'aaaaa')  # 'aaaa'
re.search(r'a{2,4}', 'aa')  # None
re.search(r'a{2,4}', 'aaa')  # 'aaa'

# *?(最短一致)にマッチする
re.search(r'a.*?b', 'aabab')  # 'aab'
re.search(r'a.*?b', 'aababb')  # 'aab'

# +?(最短一致)にマッチする
re.search(r'a.+?b', 'aabab')  # 'aab'
re.search(r'a.+?b', 'aababb')  # 'aab'

# {n}?(最短一致)にマッチする
re.search(r'a{2}?', 'aaaa')  # 'aa'

# {m,n}?(最短一致)にマッチする
re.search(r'a{2,4}?', 'aaaaa')  # 'aa'

# \(エスケープ)にマッチする
re.search(r'\^abc', '^abc')  # '^abc'
re.search(r'\.', 'a.b')  # '.'
re.search(r'\*', 'a*b')  # '*'

# \d(数字)にマッチする
re.search(r'\d', 'abc123')  # '1'

# \D(数字以外)にマッチする
re.search(r'\D', 'abc123')  # 'a'

# \s(空白文字)にマッチする
re.search(r'\s', 'abc 123')  # ' '

# \S(空白文字以外)にマッチする
re.search(r'\S', 'abc 123')  # 'a'

# \w(アルファベット、数字、アンダースコア)にマッチする
re.search(r'\w', 'abc_123')  # 'a'

# \W(アルファベット、数字、アンダースコア以外)にマッチする
re.search(r'\W', 'abc_123')  # '_'

# (?i)(大文字小文字を区別しない)にマッチする
re.search(r'(?i)hello', 'HELLO')  # 'HELLO'

# (?P<name>)(グルーピング)にマッチする
m = re.search(r'(?P<name>Mr\.|Mrs\.|Ms\.) (?P<first_name>\w+) (?P<last_name>\w+)', 'Mr. John Smith')
m.group('name')  # 'Mr.'
m.group('first_name')  # 'John'
m.group('last_name')  # 'Smith'

# |(論理和)にマッチする
re.search(r'cat|dog', 'I love my cat and my dog')  # 'cat'

# ^(行の先頭)にマッチする
re.search(r'^abc', 'abc')  # 'abc'
re.search(r'^abc', '123abc')  # None

# $(行の末尾)にマッチする
re.search(r'abc$', 'abc')  # 'abc'
re.search(r'abc$', '123abc')  # None

# \b(単語の境界)にマッチする
re.search(r'\bcat\b', 'I love my cat')  # 'cat'
re.search(r'\bcat\b', 'I love my cats')  # None

# ( )(グループ化)にマッチする
re.search(r'(cat|dog)', 'I love my cat and my dog')  # 'cat'

# (?: )(グループ化だがキャプチャしない)にマッチする
m = re.search(r'(?:cat|dog) (\w+)', 'I love my cat Tom')
m.group(1)  # 'Tom'

# (?= )(先読み)にマッチする
re.search(r'cat(?=fish)', 'I love my catfish')  # 'cat'
re.search(r'cat(?=fish)', 'I love my cat')  # None

# (?! )(否定先読み)にマッチする
re.search(r'cat(?!fish)', 'I love my cat')  # 'cat'
re.search(r'cat(?!fish)', 'I love my catfish')  # None

# (?<= )(後読み)にマッチする
re.search(r'(?<=cat)fish', 'I love my catfish')  # 'fish'
re.search(r'(?<=cat)fish', 'I love my fish')  # None

# (?<! )(否定後読み)にマッチする
re.search(r'(?<!cat)fish', 'I love my fish')  # 'fish'
re.search(r'(?<!cat)fish', 'I love my catfish')  # None

# {}(指定回数の繰り返し)にマッチする
re.search(r'a{3}', 'aaa')  # 'aaa'
re.search(r'a{3}', 'aa')  # None
re.search(r'a{2,4}', 'aa')  # None
re.search(r'a{2,4}', 'aaaaa')  # 'aaaa'
re.search(r'a{2,}', 'a')  # None
re.search(r'a{2,}', 'aa')  # 'aa'

# *(0回以上の繰り返し)にマッチする
re.search(r'a*b', 'b')  # 'b'
re.search(r'a*b', 'ab')  # 'ab'
re.search(r'a*b', 'aab')  # 'aab'
re.search(r'a*b', 'aaab')  # 'aaab'

# +(1回以上の繰り返し)にマッチする
re.search(r'a+b', 'b')  # None
re.search(r'a+b', 'ab')  # 'ab'
re.search(r'a+b', 'aab')  # 'aab'
re.search(r'a+b', 'aaab')  # 'aaab'

# ?(0回または1回の出現)にマッチする
re.search(r'a?b', 'b')  # 'b'
re.search(r'a?b', 'ab')  # 'ab'
re.search(r'a?b', 'aab')  # None
re.search(r'a?b', 'aaab')  # None

# .*(任意の文字列にマッチ)にマッチする
re.search(r'a.*b', 'ab')  # 'ab'
re.search(r'a.*b', 'a123b')  # 'a123b'
re.search(r'a.*b', 'acdb')  # None

# .+(任意の文字列にマッチ(最低1回はマッチ))にマッチする
re.search(r'a.+b', 'ab')  # 'ab'
re.search(r'a.+b', 'a123b')  # 'a123b'
re.search(r'a.+b', 'acdb')  # None

# .+?(最短一致)にマッチする
re.search(r'a.+?b', 'a123b123b')  # 'a123b'
re.search(r'a.+?b', 'a123b123bcdb')  # 'a123b'

# 繰り返しの指定(非貪欲)
re.search(r'a{2,5}?', 'aaaaa')  # 'aa'
re.search(r'a+?', 'aaaa')  # 'a'
re.search(r'a*?', 'aaaa')  # ''

# グループ化
re.search(r'(abc)+', 'abc')  # 'abc'
re.search(r'(abc)+', 'abcabc')  # 'abcabc'
re.search(r'(abc)+', 'ab')  # None

# バックリファレンス
re.search(r'([a-z])\1', 'aa')  # 'aa'
re.search(r'([a-z])\1', 'ab')  # None
re.search(r'([a-z])\1+', 'abb')  # 'bb'

# 先読み
re.search(r'a(?=b)', 'ab')  # 'a'
re.search(r'a(?!b)', 'ac')  # 'a'

# 後読み
re.search(r'(?<=a)b', 'ab')  # 'b'
re.search(r'(?<!a)b', 'cb')  # 'b'

まとめ

Python正規表現について、基本的な使い方から応用までを解説しました。正規表現は非常に強力なツールであり、文字列処理において大きな助けとなります。今後の学習の方向性としては、正規表現の応用的な使い方や、reモジュール以外の正規表現ツールの学習が挙げられます。

コメント

タイトルとURLをコピーしました