Python中級プログラミング(2)
2023/11/04
正規表現の前にエスケープシーケンスとは
  • 正規表現の前にエスケープシーケンス(Escape Sequence)について。文字列には「文字として表記できない特別な文字」があります。例えば「改行」などです。

  • 改行を表すのは \n (*1) ですが、これは 'n' という文字の前に '\' という特殊文字を付けることで、'n' という文字本来の意味を打ち消し(エスケープ)、'\' から続く文字との並び(シーケンス)に特別な意味を持たせます。これをエスケープシーケンスと呼びます。

  • Pythonの文字列では、下記が代表的なエスケープシーケンスです。
  • エスケープシーケンス 機能
    \n改行
    \r復帰(行先頭への移動)
    \tタブ
    \bバックスペース
    \\'\'文字自身(エスケープのエスケープ)
    \8進値8進値コードで表される文字(例:\115=M')
    \x16進値16進値コードで表される文字(例:\x4D='M')

  • 何故ここでエスケープシーケンスの話をしているかですが、前回の正規表現使用例からわかるように、正規表現は '\' を多用します。正規表現パターンを普通に文字列として記述すると、全てエスケープシーケンスと解釈されてしまうのです。

  • Pythonでは、文字列リテラルの接頭に'r'を付けることで、文字列中のエスケープシーケンスを特別扱いせず「そのまま(raw)」の文字列と解釈します。raw文字列と呼びます。正規表現パターンはraw文字列による表記が便利です。
  •  [Pythonコード例]
       str_a =  '改行\nと解釈される'	# 普通の文字列 
       str_b = r'改行\nと解釈されない'	# raw文字列
    
       print('str_a =', str_a)
       print('str_b =', str_b)
     [出力]
       str_a = 改行
       と解釈される
       str_b = 改行\nと解釈されない 

正規表現のコンポーネント
文字クラス
    正規表現 説明
    a re.search(r"a", "012abc") # マッチ
    re.search(r"a", "012bcd") # None
    文字'a'にマッチ
    abc re.search(r"abc", "012abcdef") # マッチ
    re.search(r"abc", "012abdghi") # None
    文字列'abc'にマッチ
    ab|cd re.search(r"ab|cd", "01abdef") # マッチ
    re.search(r"ab|cd", "01acdfg") # マッチ
    re.search(r"ab|cd", "01acbdi") # None
    文字列'ab'又は'cd'にマッチ
    [abc] re.search(r"[abc]", "01adefg") # マッチ
    re.search(r"[abc]", "012cdfg") # マッチ
    re.search(r"[abc]", "01fghig") # None
    文字'a','b'又は'c'にマッチ
    [a-z] re.search(r"[a-z]", "01-@y2") # マッチ
    re.search(r"[a-z]", "01-@23") # None
    文字'a'〜'z'にマッチ
    [^ab] re.search(r"[^ab]", "aabcab") # マッチ
    re.search(r"[^ab]", "aabaab") # None
    文字'a'又は'b'以外にマッチ
    '^'は[]の外では違う意味に
    \w re.search(r"\w", "--a @#") # マッチ
    re.search(r"\w", "--! @#") # None
    '_'と英数文字にマッチ
    [a-zA-Z0-9_]と同じ
    \W re.search(r"\W", "012#abc") # マッチ
    re.search(r"\W", "012_abc") # None
    '_'と英数文字以外にマッチ
    [^a-zA-Z0-9_]と同じ
    \d re.search(r"\d", "abc3def") # マッチ
    re.search(r"\d", "abc.def") # None
    数字にマッチ
    [0-9]と同じ
    \D re.search(r"\D", "012a456") # マッチ
    re.search(r"\D", "0123456") # None
    数字以外にマッチ
    [^0-9]と同じ
    \s re.search(r"\s", "abc def") # マッチ
    re.search(r"\s", "abc.def") # None
    空白文字にマッチ
    [\n\r\t\f\v]と同じ
    \S re.search(r"\S", "abcdef") # マッチ
    re.search(r"\S", "\n") # None
    空白文字以外にマッチ
    [^\n\r\t\f\v]と同じ
    . re.search(r".", "  ") # マッチ
    re.search(r".", "\n") # None
    改行(\n)以外の全ての文字にマッチ
    [^\n]と同じだがDOTALLモードでは扱い異なる

量指定
    正規表現 説明
    * re.search(r"ab*", "012acd") # マッチ
    re.search(r"ab*", "012abc") # マッチ
    re.search(r"ab*", "012bcd") # None
    直前の正規表現'0'文字以上にマッチ
    + re.search(r"ab+", "012abcd") # マッチ
    re.search(r"ab+", "012abbc") # マッチ
    re.search(r"ab+", "012bcde") # None
    直前の正規表現'1'文字以上にマッチ
    ? re.search(r"-?\d+", "-10") # マッチ
    re.search(r"-?\d+", " 10") # マッチ
    直前の正規表現'0'文字及び'1'文字にマッチ
    {n} re.search(r"\d{3}", "1000") # マッチ
    re.search(r"\d{3}", "  10") # None
    直前の正規表現'n'文字にマッチ
    {n,} re.search(r"\d{3,}", "1000") # マッチ
    re.search(r"\d{3,}", "  10") # None
    直前の正規表現'n'文字以上にマッチ
    {n,m} re.search(r"\d{3,4}", "100") # マッチ
    re.search(r"\d{3,4}", "1000000") # マッチ
    直前の正規表現
    'n'文字以上、'm'文字以下にマッチ

位置指定
    正規表現 説明
    ^ re.search(r"^a", "abc012") # マッチ
    re.search(r"^a", "012bcd") # None
    文字列の先頭にマッチ
    MULTILINEモードでは改行直後にもマッチ
    $ re.search(r"a$", "bc012a") # マッチ
    re.search(r"a$", "012bcd") # None
    文字列の末尾又は末尾改行直前にマッチ
    MULTILINEモードでは改行直前にもマッチ
    \A re.search(r"\A\d", "1.23") # マッチ
    re.search(r"\A\d", "e100") # None
    文字列の先頭にのみマッチ
    \b re.search(r"\bRun\b", "On The Run") # マッチ
    re.search(r"\bRun\b", "Running Away") # None
    文字列の先頭/末尾/単語境界にマッチ
    境界とは\w⇔\Wの変化点を指す
    \B re.search(r"\BRun\B", "ImRunning") # マッチ
    re.search(r"\BRun\B", "On The Run") # None
    単語中の\wにマッチ

グループ化/参照
    正規表現 説明
    (   ) re.search(r"(\w+),(\d+)", "FF,256") # マッチ
    re.search(r"(\w+),(\d+)", "FF,ABC") # None
    括弧内の正規表現にグループ化しつつマッチ
    キャプチャ結果は後で参照/利用可能
    (?: ) re.search(r"(?:\w+),(\d+)", "FF,256") # マッチ
    re.search(r"(?:\w+),(\d+)", "FF,ABC") # None
    括弧内の正規表現にグループ化しつつマッチ
    しかしキャプチャしない
    \1,\2... re.search(r"(\d+),\1:(\w+)", "256,256:ABC") # マッチ
    re.search(r"(\d+),\1:(\w+)", "256,128:abc") # None
    指定グループ番号のキャプチャ結果を参照/利用
    後方参照と呼ばれるが...参照で良いかと

キャプチャ結果をプログラム内で利用する
  • キャプチャ結果は正規表現内だけではなく当然プログラムからも利用できます。Pythonでの利用例を示します。
  • import re
    
    # マッチ/キャプチャ結果をmatchオブジェクトで受け取る
    o_match = re.search(r"(\d+),\1:(\w+)", "256,256:ABC")
    
    # マッチ/キャプチャ時はo_matchオブジェクトに実体あるためTrue
    if o_match:
        print("group1:",o_match.group(1)) # \1 キャプチャ結果
        print("group2:",o_match.group(2)) # \2 キャプチャ結果
    else:
        print("not matched")
    group1: 256
    group2: ABC

先読み/後読みアサーション
    正規表現 説明
    (?=  ) re.search(r"(\w{3})(?=de)", "abcde") # マッチ
    re.search(r"(\w{3})(?=de)", "abcef") # None
    後続文字列が指定正規表現にマッチすることを見る
    (?= )部はキャプチャされない
    (?!  ) re.search(r"(\w{3})(?!de)", "abcef") # マッチ
    re.search(r"(\w{3})(?!de)", "abcde") # None
    後続文字列が指定正規表現にマッチしないことを見る
    (?! )部はキャプチャされない
    (?<= ) re.search(r"(?<=ab)(\w{3})", "abcde") # マッチ
    re.search(r"(?<=ab)(\w{3})", "cdefg") # None
    前の文字列が指定正規表現にマッチすることを見る
    (?<= )部はキャプチャされない
    (?<! ) re.search(r"(?<!ab)(\w{3})", "cdefg") # マッチ
    re.search(r"(?<!ab)(\w{3})", "abcde") # None
    前の文字列が指定正規表現にマッチしないことを見る
    (?<! )部はキャプチャされない

次回は正規表現オブジェクト/メソッド/フラグ設定について
  • 正規表現はPython以外でも使用できる記述方法です。今回はその要素を羅列したため長くなってしまいました。 ですが忘れてしまうこともあるため、シートにまとめておくと「ぱっと見て」思い出せるようになります。

  • 次回で正規表現の説明は終了予定ですが、Pythonとしての正規表現動作制御や、メソッドを扱いたいと思います。
Notes
  • Microsoft系OSでは、'\' と '\' は同じ文字です。フォントによって見え方が変わります。日本語フォントだと'\'、非日本語フォントだと'\'です。厳密には異なる文字コードですが、Microsoft系OSでは同じコードとして扱います。
  • 実は量指定でコンポーネントの最短マッチ版(?付き)を省略しています。
Copyright(C) 2023 Altmo
本HPについて