繰り返しパターンと任意の文字列

当サイトではアフィリエイトプログラムを利用して商品を紹介しています。

文字列の繰返しにマッチする正規表現

+* などのメタ文字を使うと、文字列の繰返しにマッチする正規表現 をつくることができます。

1回以上の繰返し

+ は直前の文字の1回以上の繰返しを意味するメタ文字です。たとえば、は+
 
ははは
はははは
あははは
 
などの文字列にマッチします。

# PYTHON_REGEX_REPEAT_MATCH

# In[1]

# 正規表現オブジェクトを作成
regex = re.compile(r'あ+')

# 検索対象となる文字列を作成
line_1 = "あ~あ。牛乳をこぼしちゃった。"

line_2 = "ああっ! しまった! 宿題を忘れた!"

# line_1を検索
f1 = regex.findall(line_1)

# line_2を検索
f2 = regex.findall(line_2)

print("line1の検索結果", f1)
print("line2の検索結果", f2)

# line1の検索結果 ['あ', 'あ']
# line1の検索結果 ['ああ']

( ) で括った文字列の後ろに+ を付けると、( ) の中の文字列が繰り返されるパターンとマッチするようになります。

# In[2]

regex = re.compile(r'(くた)+')

# 検索対象となる文字列を作成
line = "Pythonやり過ぎてくたくたに疲れた。"

# lineを検索
s = regex.search(line)

print("lineの検索結果", s)
# <_sre.SRE_Match object; span=(11, 15), match='くたくた'>

0 回以上の繰返し

* は直前の文字の 0 回以上の繰返しを意味するメタ文字です。お*
 
おっす!
おおっと!
おいおい。
 
などの文字列にマッチしますが、abcdeあいうえお など、他のどんな文字列ともマッチしてしまいます。

したがって、* は2文字以上の後ろの添えるのが一般的です。たとえば、おい!* という正規表現の場合、* の直前にある文字は ! ですから、
 
おい
おい!!
おい!!!
 
のような文字列にマッチすることになります。つまり、! はあってもなくてもいい、という条件で検索します。

# In[3]

regex = re.compile(r'おい!*')

# 検索対象となる文字列を作成
line_1 = "このお菓子は、とてもおいしいですね。"

line_2 = "おい!! 何やってるんだよ!!"

line_3 = "おいおい。そりゃないだろ。"

# line_1を検索
f1 = regex.findall(line_1)

# line_2を検索
f2 = regex.findall(line_2)

# line_3を検索
f3 = regex.findall(line_3)

print("line1の検索結果", f1)
print("line2の検索結果", f2)
print("line3の検索結果", f3)

# line1の検索結果 ['おい']
# line2の検索結果 ['おい!!']
# line3の検索結果 ['おい', 'おい']

繰り返し回数の指定

{m,n} は直前の文字の m 回以上、n 回以下の繰り返しを表すメタ文字です。たとえば、わは{3,5} が 3 回以上、5 回以下の文字列にマッチします。つまり、
 
わはははは
 
のような文字列にはマッチしますが、
 
わはは
 
にはマッチしません。

# In[4]

regex = re.compile(r'わは{3,5}')

# 検索対象となる文字列を作成
line = "わはは。わはははは。"

# sentenceを検索して適合した文字列をすべて取得
f = regex.findall(line)

print(f)
# ['わはははは']

n を省略して {m,} のように書くこともできます。{0,}* と、{1,}+ と同じ意味をもちます。

あらゆる文字列にマッチする正規表現

.* は「改行以外の任意の 1 文字の 0 回以上の繰返し」なので、改行を除くあらゆる文字列にマッチするメタ文字 です。このような正規表現は、たとえば「住所:」や「氏名:」などの後に続く文字列を取り出したいときなどに使います。

# In[5]

regex = re.compile('住所:(.*)')

# 住所
line = '住所:東京都豊島区南池袋 x-y-z あとりえこばと出版'

# グループ1に適合する文字列を取得
g1 = regex.search(line).group(1)

print(g1)
# 東京都豊島区南池袋 x-y-z あとりえこばと出版

re.compile() のフラグに re.S または re.DOTALL を指定すると、. は改行を含む任意の 1 文字を表すメタ文字になります。

# In[6]

# 正規表現オブジェクトを作成
# フラグにre.Sを渡して改行を含むように設定
regex = re.compile('氏名:(.*)\n住所:(.*)', re.S)

# 検索対象文字列
my_data = '氏名:城戸涼音\n\
住所:東京都文京区小石川 x-y-z'

# my_dataを検索
s = regex.search(my_data)

# グループ1に適合する文字列を取得
g1 = s.group(1)

# グループ2に適合する文字列を取得
g2 = s.group(2)

print(g1)
print(g2)

# 城戸涼音
# 東京都文京区小石川 x-y-z

コメント

  1. norimitsu. より:

    別件ですが、ディレクトリ作成方法 分からなくて困っています。
    vn = int(input”数字入力してください”)#4桁の数字を入力する。

    save_dir =”./vn”
    os.mkdir(save_dir)
    実行結果は ”vn”のフォルダーが作成されました。 目的はnnnnのフォルダーを作成。
    入力した数字4桁 nnnn を vn に置き換えたい、どの様すると良いのかを知りたい。

    • BlogCat より:

      次のコードを試してみてください。
      vn = input(“数字入力してください”)
      save_dir = “./{}”.format(vn)
      os.mkdir(save_dir)

  2. HNaito より:

    「0 回以上の繰り返し」の説明で、「つまり、! はあってもなくてもいい」という解説は、今まで「0回」の解釈で何となく暗中模索だった自分には光が射した思いでした。

    「繰り返し回数の指定」の説明文で、
    {m, n} は m 回以上、n 回以下を表す → {m, n} は直前の文字の m 回以上、n 回以下の繰り返しを表す
    のほうが、説明文が統一されてわかりやすいと思いました。

    • あとりえこばと より:

      私も初めて「0回以上の繰り返し」という説明文を読んだ時に頭が大変混乱したことを覚えています。プログラミングの説明って、こういう独特な表現をすることが多いですよね。わからないときには、自分であれこれ試行錯誤で実行してみて「ああ、こういうことなんだ」と納得してから、なるべくわかりやすい表現を探すようにしています。

      「{m, n} は m 回以上、n 回以下を表す」はひどい説明文でしたね … 直しておきました。いつも的確なご指摘をしていただき、ありがとうございます。m(_ _)m

  3. HNaito より:

    最近気づいたのですが、回数指定の{ }内に空白を書いてしまうとちゃんとマッチできなくなるようです。たとえば REGEX_REPEAT_MATCH_04 の {3,5} のどこでも{ }内にスペースを入れてしまうと regex.findall(line) は []を返してきます。ドキュメントを見てもとくに何も書いてないようなのですが、原因をご教示いただけるとありがたいです。

    • HNaito より:

      一応解決しました。基本的には正規表現中の空白は無視されない。無視してほしければ、re.compile( ) の第 2 引数に re.VERBOSE を渡せばこの制限は緩和されるが、{ } 内の空白は無視されないようです。

  4. あとりえこばと より:

    ありがとうございます。re.VERBOSE という引数は初めて知りました。昨日のコメントを見て私も色々調べていたのですが、正規表現だけでなく、文字列の format メソッドの {} も、空白を入れると書式設定が反映されなくなるようです。