[Python] 메타문자와 findall() #1 - 파이썬 정규표현식

    정규 표현식을 사용할 때에는 메타 문자(meta characters)라는 것을 사용한다. 

    []\.^$*+{}|()

     

    이와같이 메타문자를 사용하여 다양한 정규표현식을 만들 수 있다. 개발을 하면서 모든 정규표현식을 익힌다는 것은 비효율적이며 그때그때 프로젝트에 따라 정규표현식을 사용하는 것이 유용하기 때문에 일단 하나씩 실습을 하면서 어떤 것이 가능한지만 익히는게 중요하다.

     

    파이썬 정규표현식, 메타문자와 findall


    메타 문자

    문자 설명 예시
    [] A set of characters "[a-m]"
    \ Signals a special sequence (can also be used to escape special characters) "\d"
    . Any character (except newline character) "he..o"
    ^ Starts with "^hello"
    $ Ends with "world$"
    * Zero or more occurrences "aix*"
    + One or more occurrences "aix+"
    {} Exactly the specified number of occurrences "al{2}"
    | Either or "falls|stays"
    () Capture and group  

     

    findall()

    findall()의 경우 일치 항목이 있는 모든 값을 반환하게 된다.

     

    단순 문자열 찾기

    import re
    
    txt = "I am a boy ama "
    x = re.findall("am", txt)
    print(x)
    
    # ['am', 'am']

    findall로 문자열을 찾을 경우 아래의 실행 결과 처럼 list 형으로 찾은 값들을 출력한다. 그러나 ama -> am으로 뽑히는 것처럼 값의 원본을 출력하는 것도 아니기에 이렇게 사용할 경우 단순히 find 명령어로 문자열을 찾는것과 사실상 다를바 없어서 잘 사용하지 않는다.

     

     

    문자 패턴 찾기

    import re
    
    txt = "I am a boy"
    x = re.findall("[a-b]", txt)
    print(x)
    
    # ['a', 'a', 'b']

    이번에는 []를 사용하여 문자의 범위를 지정하는 방식이다. 위의 경우, 결과로 a,a,b가 출력 되었는데 괄호 사이에 a와 b를 하이픈으로 묶은 것은 a-b 까지의 문자를 뜻한다. 그래서 해석을 하자면 I am a boy에서 a에서 b까지의 문자를 모두 찾아라라는 명령이 되는 것이다.

     

    이번에는 I도 뽑고 싶다고 가정해보자

    x = re.findall("[a-i]", txt)
    # ['a', 'a', 'b']

    i를 넣으면 뽑힐 것 같지만 정규표현식은 대소문자를 엄격히 구분하기 때문에 이런식으로 작성하면 뽑히지 않는다. 

     

    x = re.findall("[a-I]", txt)

    i를 I로 바꿀 경우 에러가 발생하는데

     

    Traceback (most recent call last):
      File "C:/Project/steel/source-python/re_test.py", line 5, in <module>
        x = re.findall("[a-I]", txt)
      File "C:\Anaconda3\envs\saibog\lib\re.py", line 239, in findall
        return _compile(pattern, flags).findall(string)
      File "C:\Anaconda3\envs\saibog\lib\re.py", line 302, in _compile
        p = sre_compile.compile(pattern, flags)
      File "C:\Anaconda3\envs\saibog\lib\sre_compile.py", line 764, in compile
        p = sre_parse.parse(p, flags)
      File "C:\Anaconda3\envs\saibog\lib\sre_parse.py", line 948, in parse
        p = _parse_sub(source, state, flags & SRE_FLAG_VERBOSE, 0)
      File "C:\Anaconda3\envs\saibog\lib\sre_parse.py", line 443, in _parse_sub
        itemsappend(_parse(source, state, verbose, nested + 1,
      File "C:\Anaconda3\envs\saibog\lib\sre_parse.py", line 598, in _parse
        raise source.error(msg, len(this) + 1 + len(that))
    re.error: bad character range a-I at position 1
    
    Process finished with exit code 1
    

    그 이유는 간단하다. 아스키 코드 순서로 적어야 되는데 대문자가 소문자보다 아스키 숫자가 작기 때문이다.

     

    x = re.findall("[A-i]", txt)
    
    # ['I', 'a', 'a', 'b']

    이렇게 대문자를 먼저 넣고, 소문자를 뒤에 넣으면 제대로 뽑히는 것을 확인할 수 있다. 하지만 이렇게 하는 것도 문제인 것이 대문자는 모두 뽑히고, 소문자는 a~i까지를 뽑게 되며 아래와 같이 일부 특수문자를 적을 경우

    txt = "I am a [[ boy"
    
    x = re.findall("[A-i]", txt)
    
    # ['I', 'a', 'a', '[', '[', 'b']

    이와같이 특수문자도 뽑히게 된다. 이는 대문자 소문자 사이에 아스키 코드 중 일부 특수문자가 끼어 있기 때문이다. 결국 이럴땐 대소문자를 구분해야 하며 만약 알파벳만 모두 뽑고 싶을 경우 다음과 같이 수정한다.

     

    x = re.findall("[a-zA-Z]", txt)
    
    # ['I', 'a', 'm', 'a', 'b', 'o', 'y', 'a', 'm', 'a']

     

     

    연속된 문자로 리턴

    위와 같은 방식을 사용하면 불편함을 느낄 것이다. 바로 문자를 하나씩 뽑게되어, 제대로 뽑혔는지 확인하기가 상당히 힘들다는 것이다. 실제 위와 같이 사용하는 케이스는 매우 적어서 아마 평생 개발하면서 사용할일이 있을까 싶을 정도일 것이다.

     

    이럴땐 한 문자씩 뽑지 말고, 플러스 특수문자를 넣어서 연속으로 나오는 경우 뭉쳐서 나오게 하는 것이 좋다.

     

    import re
    
    txt = "i am a boy"
    x = re.findall("[a-zA-Z]+", txt)
    print(x)
    
    # ['i', 'am', 'a', 'boy']

    +를 적용하니 한 문자씩 뽑히는 것이 단어같이 의미있게 뽑히는 것을 볼 수 있다. 다만 착각하지 말아야 되는 것이 단어형태로 뽑는게 아니라는 것이다.

     

    import re
    
    txt = "aeiejf82jw7212"
    x = re.findall("[a-zA-Z]+", txt)
    print(x)
    
    # ['aeiejf', 'jw']

    즉 위의 예시를 보면 아무런 의미없는 값들을 나열하였는데 2개의 리스트 형으로 분리되어 뽑혔다. 바로 연속된 알파벳은 뽑히지만, 숫자를 만난 경우 이를 기준으로 리스트를 분리한다는 것을 알 수 있다.

     

    참고자료

    https://www.w3schools.com/python/python_regex.asp#findall
    반응형

    댓글

    Designed by JB FACTORY