[ジャンプ2 Python]Chapter 08:正規表現は?Python正規表現の基礎と応用


正規表現の表示


正規表現(Regular Expressions)は、Python固有の構文ではなく、複雑な文字列を処理する際に使用される方法です.正規表現の学習はPythonの学習とは別の分野の課題である.

🤔 正規表現が必要な理由

주민등록번호를 포함하고 있는 텍스트가 있다.
이 텍스트에 포함된 모든 주민등록번호의 뒷자리를 * 문자로 변경해 보자.
  • の全文を空白文字(split)に分けます.
  • に分類された単語が身分証明書番号形式かどうかを調べる.
  • 単語が身分証明書番号形式の場合、後ろの位置を ``に変換します.
  • 単語を再結合します.
  • data = """
    park 800905-1049118
    kim  700905-1059119
    """
    
    result = []
    for line in data.split("\n"):
        word_result = []
        for word in line.split(" "):
            if len(word) == 14 and word[:6].isdigit() and word[7:].isdigit():
                word = word[:6] + "-" + "*******"
            word_result.append(word)
        result.append(" ".join(word_result))
    print("\n".join(result))
    🤯 頭に花が咲く
    ◇正規表現を使うと、コードが非常に簡潔になります

    正規表現の基礎、メタ文字


    元文字とは、もともとその文字の意味ではなく、特殊な用途に用いられる文字のこと.
    . ^ $ * + ? { } [ ] \ | ( )
    正規表現で上記のメタ文字を使用すると、特別な意味があります.

    📌 文字クラス

    [abc]
    # 문자 클래스를 만드는 메타 문자인 [ ] 사이에는 어떤 문자도 들어갈 수 있다.
  • 「a」には正規のフォーマットに一致する文字「a」があるので、
  • を組み合わせます.
  • 「before」には正規表現に一致する文字「b」があるため、
  • を組み合わせる.
  • 「dude」不整合
  • Dot(.)

    a.b # === "a + 모든문자 + b"
    # 정규 표현식의 Dot(.) 메타 문자는 줄바꿈 문자인 \n을 제외한 모든 문자와 매치됨을 의미한다.
  • 「aab」の「a」はすべての文字を表す  .と一致するので、正規式と組み合わせます.
  • 「a 0 b」は中間文字「0」を表す  .と一致するので、正規式と組み合わせます.
  • 「abc」と「a」と「b」の間の任意の文字が存在する必要があるこの正規表現は一致しないため、一致しません.
  • 繰り返し(*)

    ca*t
    # 이 정규식에는 반복을 의미하는 * 메타 문자가 사용되었다. 여기에서 사용한 *은 * 바로 앞에 있는 문자 a가 0부터 무한대로 반복될 수 있다는 의미이다.
    # 메모리상 2억개로 제한

    繰り返し(+)

    ca+t # === "c + a(1번 이상 반복) + t"
    # 반복을 나타내는 또 다른 메타 문자로 +가 있다. +는 최소 1번 이상 반복될 때 사용한다. 즉 *가 반복 횟수 0부터라면 +는 반복 횟수 1부터인 것이다.

    繰り返し({m,n},?)

    ca{2}t # === "c + a(반드시 2번 반복) + t"
    ca{2,5}t # === "c + a(2~5회 반복) + t"
    ab?c # === "a + b(있어도 되고 없어도 된다) + c"
    # === {0,1}

    Pythonで正規表現をサポートするreモジュール

    >>> import re
    >>> p = re.compile('ab*')
    re.compileを使用して正規表現をコンパイルします(上記の例ではab*).re.compileで返されるオブジェクトp(コンパイルされたモードオブジェクト)を使用して、その後の操作を実行します.
    すなわち,pは現在モードオブジェクトとなり,pをモードにマッチングさせるべきである.

    match


    matchメソッドは文字列の先頭から正規表現と一致するかどうかをチェックします
    import re
    >>> p = re.compile('[a-z]+')
    
    # 매치가 되는 경우
    
    >>> m = p.match("python")
    >>> print(m)
    <re.Match object; span=(0, 6), match='python'> # 잘 매치됨
    
    # 매치가 되지 않는 경우
    >>> m = p.match("3 python")
    >>> print(m)
    None

    search


    一致するオブジェクトの検索
    >>> m = p.search("3 python")
    >>> print(m)
    <re.Match object; span=(2, 8), match='python'>

    findall


    一致するstringをリストに戻す
    >>> result = p.findall("life is too short")
    >>> print(result)
    ['life', 'is', 'too', 'short']

    finditer


    一致するstringをイテレーションオブジェクト=イテレーション=ループ可能オブジェクトに戻す
    >>> result = p.finditer("life is too short")
    >>> print(result)
    <callable_iterator object at 0x01F5E390>
    >>> for r in result: print(r)
    <re.Match object; span=(0, 4), match='life'>
    <re.Match object; span=(5, 7), match='is'>
    <re.Match object; span=(8, 11), match='too'>
    <re.Match object; span=(12, 17), match='short'>
    以上の4つの方法で生成されるオブジェクトはmatchオブジェクトです

    matchオブジェクトのメソッド


    マッチングオブジェクト(=matchオブジェクト)で使用可能な方法
  • はどの文字列に一致しますか?
  • 文字列に一致するインデックスはどこからどこまでですか?

  • コンパイルオプション


    正規表現をコンパイルするときに使用できるオプション
    # 옵션 ㄴㄴ
    import re
    p = re.compile('[a-z]+')
    
    # 옵션 넣으면?
    import re
    p = re.compile('[a-z]+', 옵션을 넣는곳)

    DOTALL(S)

    .  この改行文字も含めて、すべての文字と組み合わせることができます.
    # 기존
    >>> import re
    >>> p = re.compile('a.b')
    >>> m = p.match('a\nb')
    >>> print(m)
    None
    
    # DOTALL
    >>> p = re.compile('a.b', re.DOTALL)
    >>> m = p.match('a\nb')
    >>> print(m)
    <_sre.SRE_Match object at 0x01FCF3D8>

    IGNORECASE(I)


    大文字と小文字を問わず組み合わせることができます.
    >>> p = re.compile('[a-z]', re.I)
    >>> p.match('python')
    <_sre.SRE_Match object at 0x01FCFA30>
    >>> p.match('Python')
    <_sre.SRE_Match object at 0x01FCFA68>
    >>> p.match('PYTHON')
    <_sre.SRE_Match object at 0x01FCF9F8>

    MULTILINE(M)


    複数の線と組み合わせることができます.( ^$  メタ文字の使用に関するオプション)
    # 기존
    import re
    p = re.compile("^python\s\w+")
    
    data = """python one
    life is too short
    python two
    you need python
    python three"""
    
    print(p.findall(data)) # ['python one']
    
    # MULTILINE(M)
    import re
    p = re.compile("^python\s\w+", re.MULTILINE)
    
    data = """python one
    life is too short
    python two
    you need python
    python three"""
    
    print(p.findall(data)) # ['python one', 'python two', 'python three'] 

    VERBOSE(X)


    verboseモードの使用を許可します.(正規表現の表示が容易になり、コメントなども使用できます.)
    charref = re.compile(r'&[#](0[0-7]+|[0-9]+|x[0-9a-fA-F]+);')
    
    # 예시
    charref = re.compile(r"""
     &[#]                # Start of a numeric entity reference
     (
         0[0-7]+         # Octal form
       | [0-9]+          # Decimal form
       | x[0-9a-fA-F]+   # Hexadecimal form
     )
     ;                   # Trailing semicolon
    """, re.VERBOSE)
    簡単に言えば、長い正規表現にコメントを追加できます.

    スラッシュ問題


    Python式にはスラッシュで始まる式がたくさんあるので、スラッシュの後ろの文字列は開発者の意図とは異なる場合があります
    \section # === [ \t\n\r\f\v]ection
    # \s 문자가 whitespace로 해석되어 의도한 대로 매치가 이루어지지 않는다.
    
    \\section ## \section 으로 인식함
    
    # \를 Raw String(= 특수문자를 무시한다) 이라 말해주면 됨
    >>> p = re.compile(r'\\section')

    強力な正規表現の世界に入ります。


    📌 メタ文字


    まだ表示されていないすべてのメタ文字について説明します.

    |


    |メタ文字の意味はorと同じです.A|Bの正規表現があれば、AまたはBを表す.
    >>> p = re.compile('Crow|Servo') # Crow 또는 Servo
    >>> m = p.match('CrowHello')
    >>> print(m)
    <re.Match object; span=(0, 4), match='Crow'>

    ^


    ^メタ文字は、文字列の先頭に一致することを示します.
    >>> print(re.search('^Life', 'Life is too short'))
    <re.Match object; span=(0, 4), match='Life'>
    >>> print(re.search('^Life', 'My Life'))
    None

    $


    ^メタ文字とは逆です.すなわち、$は文字列の末尾に一致することを意味する.
    >>> print(re.search('short$', 'Life is too short'))
    <re.Match object; span=(12, 17), match='short'>
    >>> print(re.search('short$', 'Life is too short, you need python'))
    None

    \b


    スペースを表すメタ文字
    >>> p = re.compile(r'\bclass\b')
    >>> print(p.search('no class at all'))  
    <re.Match object; span=(3, 8), match='class'>
    
    >>> print(p.search('the declassified algorithm'))
    None

    📌 ひっくり返す


    ()結合式の使用
    繰り返しabcを探していたら?
    (ABC)+
    
    >>> p = re.compile('(ABC)+')
    >>> m = p.search('ABCABCABC OK?')
    >>> print(m)
    <re.Match object; span=(0, 9), match='ABCABCABC'>
    >>> print(m.group())
    ABCABCABC
    
    # 그루핑중 원하는 그룹만 뽑아 올수있다
    
    >>> p = re.compile(r"(\w+)\s+\d+[-]\d+[-]\d+")
    >>> m = p.search("park 010-1234-1234")
    >>> print(m.group(1))
    park
    
    # 그루핑된 문자열 재참조하기
    >>> p = re.compile(r'(\b\w+)\s+\1') # (그룹) + " " + 그룹과 동일한 단어
    # 재참조 메타 문자 \1, \1은 정규식의 그룹 중 첫 번째 그룹을 가리킨다.
    >>> p.search('Paris in the the spring').group()
    'the the'
    
    # 그루핑된 문자열에 이름 붙히기 (?P<그룹명>...)
    >>> p = re.compile(r"(?P<name>\w+)\s+((\d+)[-]\d+[-]\d+)")
    >>> m = p.search("park 010-1234-1234")
    >>> print(m.group("name"))
    park

    📌 ぜんぽうたんさく


    肯定型:(?=)


    検索には含まれますが、検索結果には含まれません.
    >>> p = re.compile(".+(?=:)") # 조건에선 찾되, 결과에선 빠지게 하고 싶을때. (?=:)는 :를 제외 한다는말
    >>> m = p.search("http://google.com")
    >>> print(m.group())
    http

    否定型:(?!)


    検索から除外された出力
    import re
    
    p = re.compile(".*[.](?!bat$|exe$).*$",re.M)
    l = p.findall("""
    autoexec.exe
    autoexec.bat
    autoexec.jpg
    """)
    print(l)

    📌 置換文字列


    正規式に合わせた部分を他の文字に簡単に変えることができます.
    >>> p = re.compile('(blue|white|red)') # 정규식
    >>> p.sub('colour', 'blue socks and red shoes') # 위 정규식과 2번째 인자가 일치하는 부분을 1인자로 바꾼다
    'colour socks and colour shoes'

    📌 Greedy vs Non-Greedy

    import re 
    >>> s = '<html><head><title>Title</title>'
    >>> len(s)
    32
    >>> print(re.match('<.*>', s).span()) # Greedy '<html><head><title>Title</title>' 최대에서 가장 첫번째<>안의 문자를 가져옴
    (0, 32)
    >>> print(re.match('<.*>', s).group()) # Non-Greedy '<html><head><title>Title</title>'<>가 최소로 가장 첫번째 문자를 가져옴
    <html><head><title>Title</title>