ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [코딩도장] day30. 파이썬 정규표현식 사용하기 - re, *, +, match
    IT/파이썬 2020. 9. 29. 07:36

    출처 : unsplash

     

    ■ 들어가기

    - 파이썬 코딩 도장 (남재윤/길벗). 을 공부하며 정리하는 블로그


    ▶ Unit43. 정규표현식 사용하기

         - 43.1 문자열 판단하기

         - 43.2 범위 판단하기

         - 43.3 그룹 사용하기

         - 43.4 문자열 바꾸기

     

     

    0. 들어가기

    - 정규표현식(regular expression) : 일정한 규칙(패턴)을 가진 문자열을 표현하는 방법

    - 복잡한 문자열 속에서 특정한 규칙으로 된 문자열을 검색한 뒤 추출하거나 바꿀 때 사용

    - 또는 문자열이 정해진 규칙에 맞는지 판단하는 용도로 사용

     

     

    >> 43.1 문자열 판단하기 <<

    - 정규표현식은 re 모듈을 가져와서 사용하며

    - match 함수에 정규표현식 패턴과 판단할 문자열을 넣음 (re : regular expression의 약자)

    - 사용법: re.match('패턴', '문자열')

    >>> import re
    >>> re.match('Hello', 'Hello world!')  # Hello world! 문자열에 Hello가 있는지 판단 (있으면 정규표현식 객체가 반환)
    <_sre.SRE_Match object; span=(0, 5), match='Hello'>
    >>> re.match('Python', 'Hello world!')  # Hello world! 문자열에 Python이 있는지 판단 (없으면 아무것도 반환하지 않음)
    >>> 

    - 문자열이 있으면 매치(SRE_Match) 객체가 반환, 없으면 아무것도 반환되지 않음

    - 'Hello world!'.find('Hello') 처럼 문자열 메서드로도 사용 가능

     

     

    1. 문자열이 맨 앞에 오는지 맨 뒤에 오는지 판단하기

    - 문자열 앞에 ^를 붙이면 문자열이 맨 앞에 오는지 판단하고

    - 문자열 뒤에 $를 붙이면 문자열이 맨 뒤에 오는지 판단(=> 특정 문자열로 끝나는지)

    - 사용법

    ^문자열

    문자열$

    - 위의 패턴 이용 시 search 함수 사용해야 함

    - match 함수는 문자열 처음부터 매칭되는지 판단, search 함수는 문자열 일부분이 매칭되는지 판단

    - 사용법 : re.search('패턴', '문자열')

    >>> import re
    >>> re.search('^Hello', 'Hello, world!')  # Hello로 시작하므로 패턴에 매칭됨
    <_sre.SRE_Match object; span=(0, 5), match='Hello'>
    >>> re.search('world!$', 'Hello, world!')  # world!로 끝나므로 패턴에 매칭됨
    <_sre.SRE_Match object; span=(7, 13), match='world!'>

     

    2. 지정된 문자열이 하나라도 포함되는지 판단하기

    - | 는 특정 문자열에서 지정된 문자열(문자)이 하나라도 포함되는지 판단 (OR 연산자와 동일)

    - 사용법

    문자열|문자열

    문자열|문자열|문자열|문자열

    >>> import re
    >>> re.match('hello|world', 'hello')  # hello 또는 world가 있으므로 패턴에 매칭됨
    <_sre.SRE_Match object; span=(0, 5), match='hello'>

     

     

    >> 43.2 범위 판단하기 <<

    - 문자열이 숫자로 되어있는지 판단하기 위해서는

    - [](대괄호) 안에 숫자 범위를 넣고, * 또는 + 를 붙힘

    - 숫자 범위는 0-9 와 같이 표현하고 *는 문자(숫자)가 0개 이상 있는지, +는 1개 이상 있는지 판단

    - 사용법

    [0-9]*

    [0-9]+

    >>> import re
    >>> re.match('[0-9]*', '1234')                                     # 1234는 0부터 9까지 숫자가 0개 이상 있으므로 패턴에 매칭됨
    <_sre.SRE_Match object; span=(0, 4), match='1234'>
    >>> re.match('[0-9]+', '1234')                                     # 1234는 0부터 9까지 숫자가 1개 이상 있으므로 패턴에 매칭됨
    <_sre.SRE_Match object; span=(0, 4), match='1234'>
    >>> re.match('[0-9]+', 'abcd')                                    # abcd는 0부터 9까지 숫자가 1개 이상 없으므로 패턴에 매칭되지 않음
    >>> 

    - *+ 활용 예제

    >>> import re
    >>> re.match('a*b', 'b')                                               # b에는 a가 0개 이상 있으므로 패턴에 매칭
    <_sre.SRE_Match object; span=(0, 1), match='b'>
    >>> re.match('a+b', 'b')                                               # b에는 a가 1개 이상 없으므로 패턴에 매칭되지 않음
    >>> re.match('a*b', 'aab')                                           # aab에는 a가 0개 이상 있으므로 패턴에 매칭
    <_sre.SRE_Match object; span=(0, 3), match='aab'>
    >>> re.match('a+b', 'aab')                                           # aab에는 a가 1개 이상 있으므로 패턴에 매칭
    <_sre.SRE_Match object; span=(0, 3), match='aab'>
    >>> 

     

    1. 문자가 한 개만 있는지 판단하기

    - 문자가 여러 개 있는지 판단할 때는 *+ 를 사용했음

    - 문자가 한 개만 있는지 판단할 때는 ?. 를 사용함

    - ? 는 ? 앞의 문자(범위)가 0개 또는 1개인지 판단

    - .은 . 이 있는 위치에 아무 문자(숫자)가 1개 있는지 판단

    - 사용법

    문자?

    [0-9]?

    .

    >>> import re
    >>> re.match('abc?d', 'abd')  # abd에서 c 위치에 c가 0개 있으므로 패턴에 매칭됨
    <_sre.SRE_Match object; span=(0, 3), match='abd'>
    >>> re.match('ab[0-9]?c', 'ab3c')  # [0-9] 위치에 숫자가 1개 있으므로 패턴에 매칭됨
    <_sre.SRE_Match object; span=(0, 4), match='ab3c'>
    >>> re.match('ab.d', 'abxd')  # .이 있는 위치에 문자가 1개 있으므로 패턴에 매칭됨
    <_sre.SRE_Match object; span=(0, 4), match='abxd'>

     

    2. 문자 개수 판단하기

    - 문자(숫자)가 정확히 몇 개 있는지 판단하고 싶은 경우, 문자 뒤에 {개수} 형식으로 지정

    - 문자열의 경우 문자열을 괄호로 묶고 뒤에 {개수} 형식을 지정

    - 사용법

    문자{개수}

    (문자열){개수}

    >>> import re
    >>> re.match('h{3}', 'hhhello')                                                  # h가 3개 있는지 판단하며, 일치하는 경우 결과 반환
    <_sre.SRE_Match object; span=(0, 3), match='hhh'>
    >>> re.match('h{3}', 'hello')                                                       # 불일치하는 경우 반환값 없음
    >>>
    >>> re.match('(hello){3}', 'hellohellohelloworld')                # hello가 3개 있는지 판단
    <_sre.SRE_Match object; span=(0, 15), match='hellohellohello'>

    - 특정 범위의 문자(숫자)가 몇 개 있는지 판단 가능하며 범위 [] 뒤에 {개수} 형식을 지정

    - 사용법 : [0-9]{개수}

    >>> import re
    >>> re.match('[0-9]{3}-[0-9]{4}-[0-9]{4}', '010-1000-1000')                      # 숫자패턴 3개-4개-4개 형식 판단
    <_sre.SRE_Match object; span=(0, 13), match='010-1000-1000'>
    >>> re.match('[0-9]{3}-[0-9]{4}-[0-9]{4}', '010-1000-100')                        # 숫자패턴 3개-4개-4개 형식 판단, 불일치
    >>> 

    - 문자(숫자)의 개수 범위 지정 가능하며, {시작개수,끝개수} 형식을 지정하여 판단

    - 사용법

    (문자){시작개수,끝개수}

    (문자열){시작개수,끝개수}

    [0-9]{시작개수,끝개수}

    >>> import re
    >>> re.match('[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}', '02-100-1000')                   # 숫자 2~3개-3~4개-4개 형식 판단
    <_sre.SRE_Match object; span=(0, 11), match='02-100-1000'>
    >>> re.match('[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}', '02-10-1000')                    # 숫자 2~3개-3~4개-4개 형식 판단, 불일치
    >>> 

     

     

    3. 숫자와 영문 문자를 조합해서 판단하기

    - 영문 문자 범위 표기 : a-z, A-Z

    >>> import re
    >>> re.match('[a-zA-Z0-9]+', 'Hello1234')                                                 # a~z, A~Z, 0~9까지 1개 이상있는지 판단
    <_sre.SRE_Match object; span=(0, 9), match='Hello1234'>
    >>> re.match('[A-Z0-9]+', 'hello')                                                               # A~Z, 0~9까지 1개 이상 있는지 판단, 불일치
    >>> 

    - 한글 문자 범위 표기 : 가-힣

    >>> import re
    >>> re.match('[가-힣]+', '홍길동')                                                                 # 가~힣까지 1개 이상 있는지 판단, 매칭
    <_sre.SRE_Match object; span=(0, 3), match='홍길동'>

     

     

    4. 특정 문자 범위에 포함되지 않는지 판단

    - 특정 문자(숫자) 범위 포함 제외는 범위 앞에 ^ 를 붙히면 됨

    - 사용법

    [^범위]*

    [^범위]+

    - 예제 : [^A-Z]+ /* 대문자를 제외한 모든 문자(숫자)가 1개 이상 있는지 판단 => 즉, 대문자가 아닌 문자(숫자)로 이루어졌는지 확인 */

    >>> import re
    >>> re.match('[^A-Z]+', 'Hello')                                       # 대문자를 제외한 문자로 구성되었는지 판단. 아니므로 반환값 없음
    >>> re.match('[^A-Z]+', 'hello')                                        # 대문자가 아닌 소문자로 구성되었기 때문에 패턴에 매칭
    <_sre.SRE_Match object; span=(0, 5), match='hello'>

    [잠깐!] ^ 의 사용 범위 - 시작 vs 제외 ??

    - 특정 문자열로 시작하는지 판단 : [](대괄호) 에 붙혀줌

    - 특정 문자(숫자) 범위 제외할 때 : [](대괄호) 에 넣어줌

    >>> import re
    >>> re.match('^[A-Z]+', 'Hello')                                             # 대문자로 시작하는지 판단. 패턴에 매칭됨
    <_sre.SRE_Match object; span=(0, 1), match='H'>
    >>> re.match('[^A-Z]+', 'Hello')                                             # 대문자를 제외한 문자가 포함되었는지 판단. 패턴에 매칭되지 않음
    >>> 

    - 특정 문자(숫자) 범위로 끝나는지 확인 : 정규표현식 뒤에 $를 붙힘

    - 단, match 함수가 아닌 search 함수를 사용해야 함

    - match 함수는 문자열 처음부터 매칭되는지 판단

    - search 함수는 문자열 일부분이 매칭되는지 판단 (문자열 맨 앞, 맨 뒤 판단 시에 사용!)

    [범위]*$

    [범위]+$

    >>> import re
    >>> re.search('[0-9]+$', 'Hello1234')
    <_sre.SRE_Match object; span=(5, 9), match='1234'>

     

     

    5. 특수 문자 판단하기

    - 특수 문자 판단 시, 특수 문자 앞에 \ 를 붙힘

    - 사용법 : \특수문자

    >>> import re
    >>> re.search('\*+', '1 ** 2')                                                                        # 특수문자 * 가 포함되어 있는지 판단
    <_sre.SRE_Match object; span=(2, 4), match='**'>
    >>> re.match('[$()a-zA-Z0-9]+', '$(document)')                                     # [] 내에서는 \를 제외가능
    <_sre.SRE_Match object; span=(0, 11), match='$(document)'>
    >>> re.match('[\$()a-zA-Z0-9]+', '$(document)')                                    # [] 내에서는 \ 제외 가능하며, \를 사용해도 동일한 결과
    <_sre.SRE_Match object; span=(0, 11), match='$(document)'>

    - a-zA-Z0-9 : 영문 대소문자와 숫자.를 의미하며 아래와 같이 축약하여 표기 가능

    \d: [0-9]와 동일. 모든 숫자

    \D: [^0-9]와 동일. 숫자를 제외한 모든 문자

    \w: [a-zA-Z0-9_]와 동일. 영문 대소문자, 숫자, 밑줄 문자

    \W: [^a-zA-Z0-9_]와 동일. 영문 대소문자, 숫자, 밑줄 문자를 제외한 모든 문자

    >>> import re
    >>> re.match('\d+', '1234')                                                                 # 모든 숫자인지 판단. 매칭
    <_sre.SRE_Match object; span=(0, 4), match='1234'>
    >>> re.match('\D+', 'Hello')                                                               # 숫자를 제외한 모든 문자인지 판단. 매칭
    <_sre.SRE_Match object; span=(0, 5), match='Hello'>
    >>> re.match('\w+', 'Hello_1234')                                                     # 영문 대소문자, 숫자, 밑줄 문자인지 판단. 매칭
    <_sre.SRE_Match object; span=(0, 10), match='Hello_1234'>
    >>> re.match('\W+', '(:)')                                                                     # 영문 대소문자, 숫자, 밑줄 문자를 제외한 모든 문자인지 판단
    <_sre.SRE_Match object; span=(0, 3), match='(:)'>

     

     

    6. 공백 처리하기

    - 표현법 : ' ', \s, \S

    \s : [ \t\n\r\f\v]와 동일. 공백(스페이스), \t(탭), \n(새 줄), \r(캐리지 리턴), \f(폼피드), \v(수직 탭) 포함

    \S : [^ \t\n\r\f\v]와 동일. 공백 제외하고 \t, \n, \r, \f, \v 만 포함

    >>> import re
    >>> re.match('[a-zA-Z0-9 ]+', 'Hello 1234')                                              # ' ' 로 공백 표현
    <_sre.SRE_Match object; span=(0, 10), match='Hello 1234'>
    >>> re.match('[a-zA-Z0-9\s]+', 'Hello 1234')                                            # \s 로 공백 표현
    <_sre.SRE_Match object; span=(0, 10), match='Hello 1234'>

    [참고] 동일 정규표현식 자주 사용할 때!

    - compile 함수를 사용하여 정규표현식 패턴을 객체로 만든 뒤, match 또는 search 함수를 호출!

    - 사용법

    객체 = re.compile('패턴')

    객체.match('문자열')

    객체.search('문자열')

    >>> import re
    >>> p = re.compile('[0-9]+')                                       # 정규표현식 패턴을 객체로 만듦
    >>> p.match('1234')                                                 # 정규표현식 패턴 객체에서 match 함수 호출. 패턴 매칭
    <_sre.SRE_Match object; span=(0, 4), match='1234'>
    >>> p.search('hello')                                                 # 정규표현식 패턴 객체에서 search 함수 호출. 패턴 미매칭
    >>> 

     

     

    >> 43.3 그룹 사용하기 <<

    - 정규표현식을 개별이 아닌 그룹으로 묶는 방법

    - 패턴 안에서 정규표현식을 ()(괄호)로 묶으면 그룹이 됨

    - 사용법 : (정규표현식) (정규표현식)

    >>> import re
    >>> m = re.match('([0-9]+) ([0-9]+)', '10 295')
    >>> m.group(1)
    '10'
    >>> m.group(2)
    '295'
    >>> m.group()
    '10 295'
    >>> m.group(0)
    '10 295'

    - group 함수에 숫자를 지정하면 해당 그룹에 매칭된 문자열을 반환

    - 숫자를 지정하지 않거나 0을 지정하면 매칭된 문자열을 한꺼번에 반환

    매치객체.group(숫자)

     

    - 앞서 사용한 compile 함수를 활용하여 객체에 담아 사용 가능

    >>> p = re.compile('([0-9]+) ([0-9]+)')
    >>> p.match('10 295')
    <_sre.SRE_Match object; span=(0, 6), match='10 295'>
    >>> q = p.match('10 295')
    >>> q.group(1)
    '10'
    >>> q.group(2)
    '295'
    >>> q.group()
    '10 295'
    >>> q.group(0)
    '10 295'

     

    - groups 함수는 각 그룹에 해당하는 문자열을 튜플로 반환

    매치객체.groups()

    >>> m.groups()    # 각 그룹에 해당하는 문자열을 튜플 형태로 반환
    ('10', '295')

     

    - 그룹 개수가 많아지면 숫자로 구분하기 어렵기 때문에 그룹에 이름을 지정하여 사용

    (?P<이름>정규표현식)

    >>> import re
    >>> m = re.match('(?P<func>[a-zA-Z_][a-zA-Z0-9_]+)\((?P<arg>\w+)\)', 'print(1234)')
    >>> m.group('func')
    'print'
    >>> m.group('arg')
    '1234'
    >>> m.group()
    'print(1234)'

    매치객체.group('그룹이름')

    - 각 그룹에 설정한 이름을 group 함수에 이름을 지정하여 호출

    - 함수 이름은 영문자로 시작하기 때문에 [a-zA-Z_] 로 표현, 이후 영문자, 숫자 포함된 문자 판단

    - 괄호는 \(, \) 로 판단

     

     

    1. 패턴에 매칭되는 모든 문자열 가져오기

    - 그룹 지정 없이 패턴에 매칭되는 모든 문자열 가져올 때는 findall 함수를 사용하여 매칭된 문자열을 리스트로 반환

    - 사용법 : re.findall('패턴', '문자열')

    >>> import re
    >>> re.findall('[0-9]+', '1 2 Fizz 4 Buzz Fizz 7 8')               # 문자열에서 숫자만 가져와서 리스트로 반환
    ['1', '2', '4', '7', '8']

    [참고] *, +, 그룹 활용하기

    - 보통 정규표현식에서 +와 *을 조합하여 사용할 때는 그룹으로 묶어서 사용

    - 예를 들어 (.[a-z]+)* 는 점(.)과 영문 소문자가 1개 이상 있는지 판단하고, 이 묶음이 0개 이상인지 판단

    - 규칙은 반드시 지켜야 하지만 있어도 되고 없어도 되는 상황에서 사용

    >>> import re
    >>> re.match('[a-z]+(.[a-z]+)*$', 'hello.world')                   # 영문 소문자 1개 이상, 점과 영문 소문자1개 이상으로 끝나는지 판단
    <_sre.SRE_Match object; span=(0, 11), match='hello.world'>   # .world 는 문자열이므로 패턴에 매칭
    >>> re.match('[a-z]+(.[a-z]+)*$', 'hello.1234')                     # .1234는 숫자이므로 패턴에 미매칭
    >>> re.match('[a-z]+(.[a-z]+)*$', 'hello')                               # . 뒤에 문자열이 없어도 패턴에 매칭
    <_sre.SRE_Match object; span=(0, 5), match='hello'>

     

     

    >> 43.4 문자열 바꾸기 <<

    - 정규표현식으로 특정 문자열을 찾은 뒤, 다른 문자열로 바꾸는 방법

    - 문자열 변경 시 sub 함수를 사용하며 패턴, 바꿀 문자열, 문자열, 바꿀 횟수를 넣어줌

    - 바꿀 횟수 지정 시 지정된 횟수만큼 변경, 바꿀 횟수 생략 시 찾은 문자열 모두 변경

    - 사용법

    re.sub('패턴', '바꿀문자열', '문자열', 바꿀횟수)

    >>> import re
    >>> re.sub('apple|orange', 'fruit', 'apple box orange tree')        # apple 또는 orange를 fruit로 바꿈
    'fruit box fruit tree'
    >>> re.sub('apple|orange', 'fruit', 'apple box orange tree', 1)    # apple 또는 orange를 fruit으로 1회만 바꿈
    'fruit box orange tree'
    >>> re.sub('apple|orange', 'fruit', 'apple box orange tree', 2)   # apple 또는 orange를 fruit으로 2회만 바꿈
    'fruit box fruit tree' 
    >>> re.sub('[0-9]+', 'n', '1 2 Fizz 4 Buzz Fizz 7 8')                            # 숫자를 찾아 n 으로 변경
    'n n Fizz n Buzz Fizz n n'

    - sub 함수는 바꿀 문자열 대신에 교체 함수 지정 가능

    - 교체 함수는 매개변수로 매치 객체를 받으며, 결과를 문자열로 반환

    교체함수(매치객체)

    re.sub('패턴', 교체함수, '문자열', 바꿀횟수)

    >>> import re
    >>> def multiple10(m) :               # 매개변수로 매치 객체를 받음
    n = int(m.group())                         # 매칭된 문자열을 가져와서 정수로 변환
    return str(n * 10)                            # 숫자에 10을 곱한 뒤 문자열로 변환해서 반환

    >>> re.sub('[0-9]+', multiple10, '1 2 Fizz 4 Buzz Fizz 7 8')
    '10 20 Fizz 40 Buzz Fizz 70 80'

    - 위의 sub 함수 내용을 람다 표현식으로 사용 가능

    >>> re.sub('[0-9]+', lambda m : str(int(m.group()) * 10), '1 2 Fizz 4 Buzz Fizz 7 8')
    '10 20 Fizz 40 Buzz Fizz 70 80'

     

    1. 찾은 문자열을 결과에 다시 사용하기

    - 먼저 정규표현식을 그룹으로 묶음

    - 바꿀 문자열에서 \\숫자 형식으로 매칭된 문자열을 가져와서 사용 가능

    >>> import re
    >>> re.sub('([a-z]+) ([0-9]+)', '\\2 \\1 \\2 \\1', 'hello 1234')  # hello는 그룹1, 1234는 그룹2로 찾은 뒤 2, 1, 2, 1 순으로 변경
    '1234 hello 1234 hello'

    - 특수기호 포함된 문자열 변경

    >>> import re
    >>> re.sub('({\s*)"(\w+)":\s*"(\w+)"(\s*})', '<\\2>\\3</\\2>', '{ "name": "james" }')
    '<name>james</name>'

    - ({\s*) 는 { 와 공백(\s)을 찾음 => '{ '

    - (\s*}) 는 공백(\s)과 } 을 찾음 => ' }'

    - "(\w+)":\s*"(\w+)" 는 : 을 기준으로 양 옆의 name과 james를 찾음

    - 바꿀 문자열은 그룹 2, 그룹3, 그룹2 의 순으로 설정

    출처 : 파이썬 코딩 도장 (dojang.io)

     

    댓글

Designed by Tistory.