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

正規表現では直前の文字が指定した回数繰り返される文字列にマッチするパターンを定義することもできます。回数は 1 回以上や 0 回以上、または繰り返す回数を指定することもできます。繰り返す対象は文字の他に文字クラスなどを使ったパターン出も可能です。ここでは Python の正規表現で文字や他のパターンを指定した回数繰り返す文字列にマッチするパターンを記述する方法について解説します。

(Last modified: )

直前の文字が0回以上連続する文字にマッチ(*)

メタ文字のひとつであるアスタリスク(*)は直前の文字が 0 回以上連続する文字列にマッチします( 0 回以上というのは一度もなくてもいいし何回出てもいいという意味です)。

*

例えば次のようなパターンは最初に H 、続いて a の文字が 0 回以上続き、そのあとに n が続く文字列にマッチします。

r'Ha*n'

Han や Haaaan にはマッチします。直前の文字が 0 回以上なので Hn にもマッチします。

HnHanHaaanHaaaaaaaan
✕ Hen
✕ Ham
✕ HaaUaan
サンプルコード

簡単なサンプルで試してみます。

import re

pattern = re.compile(r'Ha*n')

print(bool(pattern.search('Hn')))
>> True
print(bool(pattern.search('Han')))
>> True
print(bool(pattern.search('Haaaaan')))
>> True
print(bool(pattern.search('HaaUaan')))
>> False

直前の文字が1回以上連続する文字にマッチする(+)

メタ文字のひとつであるプラス(+)は直前の文字が 1 回以上連続する文字列にマッチします。

+

例えば次のようなパターンは最初に H 、続いて a の文字が 1 回以上続き、そのあとに n が続く文字列にマッチします。

r'Ha+n'

Han や Haaaan にはマッチします。直前の文字が 1 回以上なので Hn にはマッチしません。

HnHanHaaanHaaaaaaaan
✕ Hen
✕ Ham
✕ HaaUaan
サンプルコード

簡単なサンプルで試してみます。

import re

pattern = re.compile(r'Ha+n')

print(bool(pattern.search('Hn')))
>> False
print(bool(pattern.search('Han')))
>> True
print(bool(pattern.search('Haaaaan')))
>> True
print(bool(pattern.search('HaaUaan')))
>> False

直前の文字が0回または1回現れる文字にマッチする(?)

メタ文字のひとつであるクエスチョンマーク(?)は直前の文字が 0 回または 1 回現れる文字にマッチします。

?

例えば次のようなパターンは最初に H 、続いて a の文字が 0 回か 1 回続き、そのあとに n が続く文字列にマッチします。

r'Ha?n'

Hn や Han にはマッチします。直前の文字が 0 回または 1 回なので Haaan にはマッチしません。

HnHan
✕ Haaan
✕ Hen
✕ Ham
サンプルコード

簡単なサンプルで試してみます。

import re

pattern = re.compile(r'Ha?n')

print(bool(pattern.search('Hn')))
>> True
print(bool(pattern.search('Han')))
>> True
print(bool(pattern.search('Haaaaan')))
>> False
print(bool(pattern.search('Ham')))
>> False

直前の文字をnum回繰り返す文字にマッチする({num})

メタ文字のひとつである {num} は直前の文字を num 回繰り返す文字列にマッチします。

{num}

例えば次のようなパターンは最初に H 、続いて a の文字が 3 回続き、そのあとに n が続く文字列にマッチします。

r'Ha{3}n'

Haaan にマッチします。繰り返される回数が決まっているので Han や Haaaaan にはマッチしません。

Haaan
✕ Han
✕ Haaaaan
サンプルコード

簡単なサンプルで試してみます。

import re

pattern = re.compile(r'Ha{3}n')

print(bool(pattern.search('Haaan')))
>> True
print(bool(pattern.search('Han')))
>> False
print(bool(pattern.search('Haaaaan')))
>> False
print(bool(pattern.search('HaaaO')))
>> False

直前の文字をmin回以上max回以下繰り返す文字にマッチする({min,max})

メタ文字のひとつである {min,max} は直前の文字を min 回以上 max 回以下繰り返す文字列にマッチします。どちらかだけを指定することもできます。

{min,}     直前の文字を min 回以上繰り返す
{,max}     直前の文字を max 回以下繰り返す
{min,max}  直前の文字を min 回以上 max 回以下繰り返す

※ {2,4} ではなく {2, 4} のように max の値の前に半角スペースを入れるとマッチしなくなりますのでご注意ください。

例えば次のようなパターンは最初に H 、続いて a の文字が 2 回以上 4 回以下繰り返し、そのあとに n が続く文字列にマッチします。

r'Ha{2,4}n'

Haan や Haaan や Haaaan にマッチします。繰り返される回数が決まっているので Han や Haaaaan にはマッチしません。

HaanHaaanHaaaan
✕ Han
✕ Haaaaan
サンプルコード

簡単なサンプルで試してみます。

import re

pattern = re.compile(r'Ha{2,4}n')

print(bool(pattern.search('Haan')))
>> True
print(bool(pattern.search('Haaaan')))
>> True
print(bool(pattern.search('Haaaaan')))
>> False
print(bool(pattern.search('HaaaO')))
>> False

直前のパターンを繰り返す

ここまで解説した繰り返しを行うメタ文字は、繰り返しの対象が直前の文字だけでなく直前の別のパターンを繰り返す文字列にもマッチします。例えば任意の文字を表すドット(.)の直後にアスタリスク(*)を記述すると、任意の文字を 0 回以上繰り返す文字列にマッチします。

.*

次のようなパターンは最初に <p> が現れ、そのあとに任意の文字が 0 回以上続き、最後に </p> が現れる文字列にマッチします。

r'<p>.*</p>'

また 0 から 9 までのいずれかの文字とマッチする文字クラスの [0-9] の直後に {4} を記述すると、任意の数字が 4 回続く文字列にマッチします。

[0-9]{4}

次のようなパターンは最初に任意の数字が 3 回続き、そのあとにハイフン(-)が続き、最後に任意の数字が 4 回続く文字列にマッチします。

r'[0-9]{3}-[0-9]{4}'
サンプルコード

簡単なサンプルで試してみます。

import re

pattern = re.compile(r'<em>.*</em>')
msg = '<p>今日は<em>快晴の</em>一日です</p>';

result = pattern.search(msg)
print(result.group(0))

>> <em>快晴の</em>

pattern = re.compile(r'[0-9]{3}-[0-9]{4}')
msg = '郵便番号は 123-4567 です';

result = pattern.search(msg)
print(result.group(0))

>> 123-4567

グループ化したパターンを繰り返す

ここまでは繰り返しの対象が直前の文字やパターンの場合でしたが、複数の文字やパターンを括弧()で囲んでグループ化することで、グループ化した複数の文字やパターンを繰り返す文字列にもマッチさせることができます。

例えば数値が 1 つから 3 つ続いたあとにドット(.)が続く文字列を 3 回繰り返すパターンを作成してみます。最初に数値が 1 つから 3 つ続いたあとにドット(.)が続く文字列のパターンは次のように記述できます(文字としてのドット(.)を使用するのでバックスラッシュ(\)を使って \. のようにエスケープしています)。

r'\d{1,3}\.'

このパターン全体を 3 回繰り返すパターンなので、単に {3} を付け加えるのではなく、対象のパターン全体を括弧()で囲んでグループ化したあとで {3} を記述します。

r'(\d{1,3}\.){3}'

これで数値が 1 つから 3 つ続いたあとにドット(.)が続く文字列を 3 回繰り返すパターンが完成です。

サンプルコード

簡単なサンプルで試してみます。

import re

pattern = re.compile(r'(\d{1,3}\.){3}\d{1,3}')
msg = 'IPアドレスは 192.168.0.18 です';

result = pattern.search(msg)
print(result.group(0))

192.168.0.18

最大量指定子と最小量指定子

+ や * などの量指定子はデフォルトではより多い文字列にマッチしようとします。次のサンプルをみてください。

import re

pattern = re.compile(r'b.*a')
msg = 'breakfast is sandwich';

result = pattern.search(msg)
print(result.group(0))

breakfast is sa

このパターンでは b で始まり、任意の文字が 0 回以上続いたあとに a が続く文字列とマッチします。対象の文字列の中で b のあとに a が現れるのは次の三か所があります。

breakfast is sandwich
breakfast is sandwich
breakfast is sandwich

先ほどのサンプルで確認できたように実際にマッチするのは一番右側にある a のところです。デフォルトで + や * や {min,max} などの量指定子はより多い文字列にマッチしようとするためです。このような量指定子のことを最大量指定子と呼びます。

+ や * などの量指定子を使用した時により少ない文字列にマッチさせる場合は最小量指定子を使用します。それぞれの量指定子のあとにクエスチョンマーク(?)を記述します。

*?
+?
??
{min, max}?

では先ほどのサンプルを最小量指定子を使って書き直してみます。

import re

pattern = re.compile(r'b.*?a')
msg = 'breakfast is sandwich';

result = pattern.search(msg)
print(result.group(0))

brea

今度のサンプルではマッチする候補の中で一番少ない文字列とマッチしました。

最大量指定子と最小量指定子が実際にどのように対象の文字列にマッチするのかについて、より詳しくは「正規表現における最大量指定子と最小量指定子の考え方」を参照されてください。

-- --

Python の正規表現で文字や他のパターンを指定した回数繰り返す文字列にマッチするパターンを記述する方法について解説しました。

( Written by Tatsuo Ikura )

プロフィール画像

著者 / TATSUO IKURA

これから IT 関連の知識を学ばれる方を対象に、色々な言語でのプログラミング方法や関連する技術、開発環境構築などに関する解説サイトを運営しています。