文字列の繰返しにマッチする正規表現
+ や * などのメタ文字を使うと、文字列の繰返しにマッチする正規表現 をつくることができます。
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
コメント
別件ですが、ディレクトリ作成方法 分からなくて困っています。
vn = int(input”数字入力してください”)#4桁の数字を入力する。
例
save_dir =”./vn”
os.mkdir(save_dir)
実行結果は ”vn”のフォルダーが作成されました。 目的はnnnnのフォルダーを作成。
入力した数字4桁 nnnn を vn に置き換えたい、どの様すると良いのかを知りたい。
次のコードを試してみてください。
vn = input(“数字入力してください”)
save_dir = “./{}”.format(vn)
os.mkdir(save_dir)
「0 回以上の繰り返し」の説明で、「つまり、! はあってもなくてもいい」という解説は、今まで「0回」の解釈で何となく暗中模索だった自分には光が射した思いでした。
「繰り返し回数の指定」の説明文で、
{m, n} は m 回以上、n 回以下を表す → {m, n} は直前の文字の m 回以上、n 回以下の繰り返しを表す
のほうが、説明文が統一されてわかりやすいと思いました。
私も初めて「0回以上の繰り返し」という説明文を読んだ時に頭が大変混乱したことを覚えています。プログラミングの説明って、こういう独特な表現をすることが多いですよね。わからないときには、自分であれこれ試行錯誤で実行してみて「ああ、こういうことなんだ」と納得してから、なるべくわかりやすい表現を探すようにしています。
「{m, n} は m 回以上、n 回以下を表す」はひどい説明文でしたね … 直しておきました。いつも的確なご指摘をしていただき、ありがとうございます。m(_ _)m
最近気づいたのですが、回数指定の{ }内に空白を書いてしまうとちゃんとマッチできなくなるようです。たとえば REGEX_REPEAT_MATCH_04 の {3,5} のどこでも{ }内にスペースを入れてしまうと regex.findall(line) は []を返してきます。ドキュメントを見てもとくに何も書いてないようなのですが、原因をご教示いただけるとありがたいです。
一応解決しました。基本的には正規表現中の空白は無視されない。無視してほしければ、re.compile( ) の第 2 引数に re.VERBOSE を渡せばこの制限は緩和されるが、{ } 内の空白は無視されないようです。
ありがとうございます。re.VERBOSE という引数は初めて知りました。昨日のコメントを見て私も色々調べていたのですが、正規表現だけでなく、文字列の format メソッドの {} も、空白を入れると書式設定が反映されなくなるようです。