ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 정규 표현식
    프로그래밍/정규 표현식 2021. 12. 7. 19:28
    re.search(r'[A-Za-z]{6}', cc_list)
    # <re.Match object; span=(5, 11), match='Koenig'>​
    cc_list = '''Ezra Koenig <ekoeni@vpwk.com>,
    Rostam Batmanglij <rostam@vpwk.com>,
    Chris Tomson <ctomson@vpwk.com>,
    Bobbi Baio <bbaio@vpwk.com>'''
    import re

     

    캐릭터 세트

    • 선택적으로 일치할 수 있는 문자는 정규 표현식의 정의에서 대괄호로 묶어서 나타낸다.
      B 또는 R로 시작해서 obb가 이어지고 i 혹은 y로 끝나는 이름을 찾을 수 있다.

     

    re.search(r'[RB]obb[iy]', cc_list)
    # <re.Match object; span=(101, 106), match='Bobbi'>

     

     
    • 콤마로 구분된 개별 문자를 입력하거나 범위를 사용할 수도 있다. A-Z는 대문자 모두를 포함하며, 0-9는 0에서 9까지의 모든 숫자를 포함한다.
     
    re.search(r'Chr[a-z][a-z]', cc_list)
    # <re.Match object; span=(68, 73), match='Chris'>
     
     
    • 어떤 항목 뒤에 있는 +는 해당 항목이 한 번 이상 나타나는 것을 찾아준다.
    • 대괄호 안의 숫자는 문자의 개수가 정확히 일치하는 것을 찾는다.
      re.search(r'[A-Za-z]+', cc_list)
      # <re.Match object; span=(0, 4), match='Ezra'>​
    • '.' 문자는 특별한 의미로 쓰여서 어떤 문자와도 매칭할 수 있는 와일드카드다. 실제로 '.' 문자와 일치하는 것을 찾기위해서는 백슬래시(\)를 사용해 이스케이프 처리를 해줘여 한다.
    re.search(r'[A-Za-z]+@[a-z]+\.[a-z]+', cc_list)
    <re.Match object; span=(13, 28), match='ekoeni@vpwk.com'>

     

    캐릭터 클래스

    파이썬의 re는 미리 만들어진 캐릭터 세트인 캐릭터 클래스를 제공한다. 일반적으로 사용되는 것은 \w로 이는 [a-zA-Z0-9_], \d는 [0-9]에 해당한다.

    re.search(r'\w+', cc_list)
    # <re.Match object; span=(0, 4), match='Ezra'>

    기본적인 이메일 매칭은 \w로 대체해서 처리할 수 있다.

    re.search(r'\w+\@\w+\.\w+', cc_list)
    # <re.Match object; span=(13, 28), match='ekoeni@vpwk.com'>

     

    그룹

    • 괄호를 사용해 일치한 대상엣 그룹을 정의할 수 있다. 이렇나 그룹은 일치한 객체에서 접근할 수 있는데, 나타난 순서대로 번호가 매겨지며 0 그룹은 전체 일치를 의미한다.
    re.search(r'(\w+)\@(\w+)\.(\w+)', cc_list)
    # <re.Match object; span=(13, 28), match='ekoeni@vpwk.com'>
    matched = re.search(r'(\w+)\@(\w+)\.(\w+)', cc_list)
    matched.group(0)
    # ekoeni@vpwk.com
    matched.group(1)
    # ekoeni
    matched.group(2)
    # vpwk
    matched.group(3)
    # com

     

    네임드 그룹

    그룹 정의에 ?P<NAME>을 추가해 해당 그룹에 일므을 부여할 수 있다. 이후부터는 번호 대신에 이름을 통해 그룹에 접근 가능하다.

    matched =re.search(r'(?P<name>\w+)\@(?P<SLD>\w+)\.(?P<TLD>\w+)', cc_list)
    matched.group('name')
    # ekoeni
    print(f'''name: {matched.group('name')}
    Secondary Level Domain: {matched.group('SLD')}
    Top Level Domain: {matched.group('TLD')}''')
    
    # name: ekoeni
    # Secondary Level Domain: vpwk
    # Top Level Domain: com

    모두 찾기

    matched = re.findall(r'\w+\@\w+\.\w+', cc_list)
    matched
    # ['ekoeni@vpwk.com', 'rostam@vpwk.com', 'ctomson@vpwk.com', 'bbaio@vpwk.com']
    matched = re.findall(r'(\w+)\@(\w+)\.(\w+)', cc_list)
    matched
    # [('ekoeni', 'vpwk', 'com'),
    # ('rostam', 'vpwk', 'com'),
    # ('ctomson', 'vpwk', 'com'),
    # ('bbaio', 'vpwk', 'com')]
    names = [x[0] for x in matched]
    names
    # ['ekoeni', 'rostam', 'ctomson', 'bbaio']

     

    찾기 반복자

     

    로그 같은 대량의 텍스트를 다룰 때는 모든 텍스트를 한꺼번에 처리하지 않는 것이 좋다. finditer 메소드를 사용해 iterator 객체를 생성할 수 있는데, 이 객체는 텍스트를 처리하다가 일치하는 항목을 찾으면 멈춘다.  현재 찾은 내용을 next 함수를 통해 반환하고 다음 일치하는 항목을 찾을 때까지 이어서 처리한다. 이와 같은 방법을 통해 모든 입력을 한 번에 처리하느라 리소스를 쏟지 않고도 일치하는 항목을 1개씩 찾을 수 있다.

     

    matched = re.finditer(r'\w+\@\w+\.\w+', cc_list)
    matched
    # <callable_iterator at 0x7f556d294790>
    next(matched)
    # <re.Match object; span=(13, 28), match='ekoeni@vpwk.com'>
    next(matched)
    # <re.Match object; span=(50, 65), match='rostam@vpwk.com'>
    next(matched)
    # <re.Match object; span=(82, 98), match='ctomson@vpwk.com'>
    matched = re.finditer(r'(?P<name>\w+)\@(?P<SLD>\w+)\.(?P<FLD>\w+)', cc_list)
    for m in matched:
        print(m.groupdict())
    
    # {'name': 'ekoeni', 'SLD': 'vpwk', 'FLD': 'com'}
    # {'name': 'rostam', 'SLD': 'vpwk', 'FLD': 'com'}
    # {'name': 'ctomson', 'SLD': 'vpwk', 'FLD': 'com'}
    # {'name': 'bbaio', 'SLD': 'vpwk', 'FLD': 'com'}

     

    치환

    정규 표현식을 사용하면 검색과 매칭 이외에도 문자열의 일부 또는 전체를 치환할 수 있다.

    re.sub('\d', '#', "The passcode you entered was 09876")
    # The passcode you entered was #####
    users = re.sub('(?P<name>\w+)\@(?P<SLD>\w+)\.(?P<TLD>\w+)', "\g<TLD>.\g<SLD>.\g<name>", cc_list)
    print(users)
    # Ezra Koenig <com.vpwk.ekoeni>,
    # Rostam Batmanglij <com.vpwk.rostam>,
    # Chris Tomson <com.vpwk.ctomson>,
    # Bobbi Baio <com.vpwk.bbaio>

     

    컴파일링

    지금까지의 예시는 re 모듈의 메소드를 직접 호출하는 방식이었다. 이는 다양한 경우에 적합하긴 하지만, 도일한 일치 항목이 많이 발생하는 경우에는 정규 표현식을 하나의 객체로 컴파일해 성능상의 이점을 얻을 수 있다. 이 객체는 재컴파일 없이 사용이 가능하다.

    regex = re.compile(r'\w+\@\w+\.\w+')
    regex.search(cc_list)
    # <re.Match object; span=(13, 28), match='ekoeni@vpwk.com'>

     

     

    출처: Python for DevOps

     

     

    고찰

    여태까지 리스트나 2차원 매트릭스에서 일치 (또는 부분 일치)하는 요소를 찾기 위해 무식하게 (for문) 수행하였다. 하지만 위에서 소개한 간단한 정규 표현식으로도 내가 여태까지 수행하였던 그 태스크들을 많은 부분 간편하게 수행할 수 있을 것이라 판단한다. 현재 정규 표현식을 더 공부할 시간은 부족하나 앞으로 지속적으로 이에 대한 지식을 쌓을 필요는 있을 것이다. 

    댓글

Designed by Tistory.