ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 정규표현식
    Python 2023. 10. 25. 17:12

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

     

    문자열 판단하기

    re 모듈을 가져와 사용(re는 regular expression의 약자)

    >>> # re.match('패턴', '문자열')
    >>>
    >>> import re
    >>>
    >>> re.match('Hello', 'Hello, world')     # 문자열이 있으므로 정규표현식 매치 객체 반환
    <re.Match object; span=(0, 5), match='Hello'>
    >>>
    >>> re.match('python', 'Hello, world')    # 문자열이 없으므로 아무것도 반환되지 않음
    >>>
    >>> re.match('python', 'Hello, python')
    >>>

     

    문자열이 맨 앞에 오는지 맨 뒤에 오는지 판단

     

    ^문자열: 문자열이 맨 앞에 오는지 판단 / 문자열$: 문자열이 맨 뒤에 오는지 판단(특정 문자열로 끝나는지)

    이때는 match 대신 search 함수를 사용해야 함

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

    >>> # re.search('패턴', '문자열')
    >>>
    >>> re.search('^Hello', 'Hello, world')    # Hello로 시작하므로 패턴에 매칭됨
    <re.Match object; span=(0, 5), match='Hello'>
    >>>
    >>> re.search('world$', 'Hello, world')    # world로 끝나므로 패턴에 매칭됨
    <re.Match object; span=(7, 12), match='world'>
    >>>
    >>> re.search('world$', 'Hello, world!')
    >>>
    >>> re.search('Python', 'Hello, Python')
    <re.Match object; span=(7, 13), match='Python'>
    >>>
    >>> re.search('world', 'Hello, world!')
    <re.Match object; span=(7, 12), match='world'>
    >>>
    >>> re.search('world', 'Hello world universe local')
    <re.Match object; span=(6, 11), match='world'>

     

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

    >>> # 문자열|문자열 / 문자열|문자열|문자열|문자열
    >>> re.match('hello|world', 'hello')
    <re.Match object; span=(0, 5), match='hello'>

     

    범위 판단하기

    문자열이 숫자로 되어 있는지 판단, 숫자 범위는 0-9처럼 표현

    >>> # [0-9]*  : 문자(숫자)가 0개 이상 있는지 판단
    >>> # [0-9]+ : 문자(숫자)가 1개 이상 있는지 판단
    >>>
    >>> re.match('[0-9]*', '1234')    # 1234는 0부터 9까지 숫자가 0개 이상 있으므로 패턴에 매칭됨
    <re.Match object; span=(0, 4), match='1234'>
    >>>
    >>> re.match('[0-9]+', '1234')    # 1234는 0부터 9까지 숫자가 1개 이상 있으므로 패턴에 매칭됨
    <re.Match object; span=(0, 4), match='1234'>
    >>>
    >>> re.match('[0-9]+', 'abcd')    # abcd는 0부터 9까지 숫자가 1개 이상 없으므로 패턴에 매칭 X
    >>>
    >>> re.match('[0-9]+', 'abcd1')    # match는 맨 앞에서부터 찾기 때문에 패턴에 매칭 x
    >>>
    >>> re.match('[0-9]+', '1abcd')
    <re.Match object; span=(0, 1), match='1'>

     

    *와 + 활용

    >>> re.match('a*b', 'b')    # b에는 a가 0개 이상 있으므로 패턴에 매칭
    <re.Match object; span=(0, 1), match='b'>
    >>>
    >>> re.match('a+b', 'b')    # b에는 a가 1개 이상 없으므로 패턴에 매칭 X
    >>>
    >>> re.match('a*b', 'aab')    # aab에는 a가 0개 이상 있으므로 패턴에 매칭
    <re.Match object; span=(0, 3), match='aab'>
    >>>
    >>> re.match('a+b', 'aab')    # aab에는 a가 1개 이상 있으므로 패턴에 매칭
    <re.Match object; span=(0, 3), match='aab'>
    >>>
    >>> re.match('a*b', 'baa')
    <re.Match object; span=(0, 1), match='b'>
    >>>
    >>> re.match('a+b', 'baa')
    >>>
    >>> re.match('a*b', 'ab')
    <re.Match object; span=(0, 2), match='ab'>
    >>>
    >>> re.match('a*b', 'ba')
    <re.Match object; span=(0, 1), match='b'>
    >>>
    >>> re.match('a+b', 'ab')
    <re.Match object; span=(0, 2), match='ab'>
    >>>
    >>> re.match('a+b', 'ba')
    >>>
    >>> re.match('ba+', 'ba')
    <re.Match object; span=(0, 2), match='ba'>
    >>>

    a*b, a+b에서 b는 무조건 있어야 하는 문자

    > a*는 a가 0개 이상 있어야 하므로 b 매칭, a+는 a가 1개 이상 있어야 하므로 b는 매칭 X

     

    문자가 한 개만 있는지 판단

    >>> # 문자?, [0-9]? : ? 앞의 문자(범위)가 0개 또는 1개인지 판단
    >>> # . : .이 있는 위치에 아무 문자(숫자)가 1개 있는지 판단
    >>>
    >>> re.match('abc?d', 'abd')    # abd에서 c 위치에 c가 0개 있으므로 패턴에 매칭
    <re.Match object; span=(0, 3), match='abd'>
    >>>
    >>> re.match('ab[0-9]?c', 'ab3c')    # [0-9] 위치에 숫자가 1개 있으므로 패턴에 매칭
    <re.Match object; span=(0, 4), match='ab3c'>
    >>>
    >>> re.match('ab.d', 'abxd')    # .이 있는 위치에 문자가 1개 있으므로 패턴에 매칭
    <re.Match object; span=(0, 4), match='abxd'>
    >>>
    >>> re.match('ab.d', 'abx1d')
    >>>
    >>> re.match('ab..d', 'abx1d')
    <re.Match object; span=(0, 5), match='abx1d'>

     

    문자 개수 판단

    >>> # 문자{개수} / (문자열){개수}
    >>>
    >>> re.match('h{3}', 'hhhello')
    <re.Match object; span=(0, 3), match='hhh'>
    >>>
    >>> re.match('(hello){3}', 'hellohellohelloworld')
    <re.Match object; span=(0, 15), match='hellohellohello'>
    >>>
    >>> re.match('h{3}', 'hhhhello')
    <re.Match object; span=(0, 3), match='hhh'>
    >>>
    >>> re.match('h{3}', 'hhello')
    >>>
    >>> re.match('(hello){3}', 'hello hello hello world')
    >>>

    특정 범위의 문자(숫자)가 몇 개 있는지 판단 가능

    >>> # [0-9]{개수}
    >>>
    >>> # 문자(숫자)의 개수 범위도 지정 가능 {시작개수,끝개수} 형식
    >>> # (문자){시작개수,끝개수} / (문자열){시작개수,끝개수} / [0-9]{시작개수,끝개수}
    >>>
    >>> re.match('[0-9]{3}-[0-9]{4}-[0-9]{4}', '010-1000-1000')    # 숫자 3개-4개-4개
    <re.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개-3개
    >>>
    >>> re.match('[0-9]{3}-[0-9]{4}-[0-9]{4}', '010.1000.1000')
    >>>
    >>> re.match('[0-9]{3}.[0-9]{4}.[0-9]{4}', '010.1000.1000')
    <re.Match object; span=(0, 13), match='010.1000.1000'>
    >>>
    >>> re.match('[0-9]{3}.[0-9]{4}.[0-9]{4}', '010--1000--1000')
    >>>
    >>> re.match('[0-9]{3}..[0-9]{4}..[0-9]{4}', '010--1000--1000')
    <re.Match object; span=(0, 15), match='010--1000--1000'>
    >>>
    >>> re.match('[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}', '02-100-1000')
    <re.Match object; span=(0, 11), match='02-100-1000'>
    >>>
    >>> re.match('[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}', '02-1000-1000')
    <re.Match object; span=(0, 12), match='02-1000-1000'>
    >>>
    >>> re.match('[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}', '02-10-1000')
    >>>

     

    숫자와 영문 문자를 조합해 판단

    >>> # a-z / A-Z
    >>>
    >>> re.match('[a-zA-Z0-9]+', 'Hello1234')    # a부터 z, A부터 Z, 0부터 9까지 1개 이상
    <re.Match object; span=(0, 9), match='Hello1234'>
    >>>
    >>> re.match('[A-Z0-9]+', 'hello')    # A부터 Z, 0부터 9까지 1개 이상 / 대문자, 숫자는 없고 소문자만 있어 매칭 x
    >>>
    >>> # 가-힣
    >>>
    >>> re.match('[가-힣]+', '홍길동')    # 가부터 힣까지 1개 이상
    <re.Match object; span=(0, 3), match='홍길동'>
    >>>
    >>> re.match('[가-힣]+', '아헿헷')    # 가부터 힣까지 1개 이상
    <re.Match object; span=(0, 3), match='아헿헷'>

     

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

    >>> # 특정 문자 범위에 포함되지 않는지 판단(제외)
    >>> # [^범위]* / [^범위]+
    >>>
    >>> re.match('[^A-Z]+', 'Hello')    # 대문자를 제외
    >>>
    >>> re.match('[^A-Z]+', 'hello')    # 대문자를 제외, 대문자가 없으므로 패턴에 매칭
    <re.Match object; span=(0, 5), match='hello'>

    범위 제외할 때는 '[^A-z]+'와 같이 [] 안에 넣어 주고, 특정 문자 범위로 시작할 때는 '^[A-Z]+'와 같이 [] 앞에 붙여 줌

    >>> # 특정 문자 범위로 시작
    >>> # ^[범위]* / ^[범위]+
    >>> re.search('^[A-Z]+', 'Hello')    # 대문자로 시작하므로 패턴에 매칭
    <re.Match object; span=(0, 1), match='H'>
    >>>
    >>> # 특정 문자 범위로 끝나는지 확인
    >>> # [범위]*$ / [범위]+$
    >>>
    >>> re.search('[0-9]+$', 'Hello1234')    # 숫자로 끝나므로 패턴에 매칭
    <re.Match object; span=(5, 9), match='1234'>

     

    특수 문자 판단

    >>> # \특수문자
    >>> # [] 안에서는 \를 붙이지 않아도 되지만 에러가 발생하는 경우 \를 붙임
    >>>
    >>> re.search('\*+', '1 ** 2')    # *가 들어 있는지 판단
    <re.Match object; span=(2, 4), match='**'>
    >>>
    >>> re.match('[$()a-zA-Z0-9]+', '$(document0')    # $, (, )와 문자, 숫자가 들어 있는지 판단
    <re.Match object; span=(0, 11), match='$(document0'>
    >>>

    범위를 지정하면서 a-zA-Z0-9처럼 대소문자와 숫자를 모두 나열했는데 이런 방식으로 범위를 정하면 정규표현식이 길고 복잡해짐

    숫자인지 문자인지 판단할 때는 \d, \D, \w, \W를 사용

     

    \d: [0-9]와 같음, 모든 숫자

    \D: [^0-9]와 같음

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

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

    >>> re.match('\d+', '1234')    # 모든 숫자이므로 패턴에 매칭
    <re.Match object; span=(0, 4), match='1234'>
    >>>
    >>> re.match('\D+', 'hello')    # 숫자를 제외한 모든 문자이므로 패턴에 매칭
    <re.Match object; span=(0, 5), match='hello'>
    >>>
    >>> re.match('\w+', 'Hello_1234')    # 영문 대소문자, 숫자, 밑줄 문자이므로 패턴에 매칭
    <re.Match object; span=(0, 10), match='Hello_1234'>
    >>>
    >>> re.match('\W+', '(:)')    # 영문 대소문자, 숫자, 밑줄 문자를 제외한 모든 문자이므로 패턴에 매칭
    <re.Match object; span=(0, 3), match='(:)'>

     

    공백 처리

    공백은 ' '처럼 공백 문자를 넣어도 되고, \s 또는 \S로 표현할 수 있음

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

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

     

    캐리지 리턴: 인쇄 위치 또는 커서 표시 위치를 같은 줄(행) 맨 앞의 위치로 복귀

    라인 피드: 커서의 위치를 아랫줄로 이동

    폼 피드: 프린터에 보내면 연속용지가 담 페이지의 시작 부분으로 넘겨짐

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

     

    같은 정규표현식 패턴을 자주 사용할 때

    매번 match나 search 함수에 정규표현식 패턴 지정하는 것은 비효율적

    같은 패턴 자주 사용할 때는 compile 함수를 사용해 정규표현식 패턴을 객체로 만든 뒤 match 또는 search 메서드 호출

    >>> # 객체 = re.compile('패턴')
    >>> # 객체.match('문자열')
    >>> # 객체.search('문자열')
    >>>
    >>> p = re.compile('[0-9]+')    # 정규표현식 패턴을 객체로 만듦
    >>> p.match('1234')             # 정규표현식 패턴 객체에서 match 메서드 사용
    <re.Match object; span=(0, 4), match='1234'>
    >>> p.search('hello')             # 정규표현식 패턴 객체에서 search 메서드 사용
    >>>

     

    그룹 사용

    >>> # 패턴 안에서 정규표현식을 ()(괄호)로 묶으면 그룹이 됨
    >>> # (정규표현식) (정규표현식)
    >>> # 공백으로 구분된 숫자를 두 그룹으로 나누어 찾은 뒤 각 그룹에 해당하는 문자열(숫자) 가져옴
    >>>
    >>> m = re.match('([0-9]+) ([0-9]+)', '10 295')
    >>> m.group(1)    # 첫 번째 그룹(그룹 1)에 매칭된 문자열 반환
    '10'
    >>> m.group(2)    # 두 번째 그룹(그룹 2)에 매칭된 문자열 반환
    '295'
    >>> m.group()    # 매칭된 문자열을 한꺼번에 반환
    '10 295'
    >>> m.group(0)    # 매칭된 문자열을 한꺼번에 반환
    '10 295'

    매치 객체의 group 메서드에 숫자를 지정하면 해당 그룹에 매칭된 문자열을 반환

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

     

    groups 메서드는 각 그룹에 해당하는 문자열을 튜플로 반환

    >>> # 매치객체.groups()
    >>>
    >>> m.groups()
    ('10', '295')

     

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

    그룹 지정 없이 패턴에 매칭되는 모든 문자열 가져오기

    > findall 함수를 사용해 매칭된 문자열을 리스트로 반환

    >>> # re.findall('패턴', '문자열')
    >>>
    >>> re.findall('[0-9]+', '1 2 Fizz 4 Buzz Fizz 7 8')
    ['1', '2', '4', '7', '8']
    >>>
    >>> re.findall('[a-z]+', '1 2 Fizz 4 Buzz Fizz 7 8')
    ['izz', 'uzz', 'izz']
    >>>
    >>> re.findall('[A-Z]+', '1 2 Fizz 4 Buzz Fizz 7 8')
    ['F', 'B', 'F']
    >>>
    >>> re.findall('[a-zA-Z]+', '1 2 Fizz 4 Buzz Fizz 7 8')
    ['Fizz', 'Buzz', 'Fizz']
    >>>
    >>> re.findall('[a-zA-Z0-9]+', '1 2 Fizz 4 Buzz Fizz 7 8')
    ['1', '2', 'Fizz', '4', 'Buzz', 'Fizz', '7', '8']
    >>>

     

     

    문자열 바꾸기

    정규표현식으로 특정 문자열 찾은 뒤 다른 문자열로 바꾸는 방법, sub 함수 사용

    >>> # re.sub('패턴', '바꿀문자열', '문자열', 바꿀횟수)
    >>> # 바꿀 횟수를 넣으면 지정된 횟수만큼 바꾸고, 생략할 경우 찾은 문자열 모두 바꿈
    >>>
    >>> re.sub('apple|orange', 'fruit', 'apple box orange tree')    # apple 또는 orange를 fruit로 바꿈
    '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.subb('패턴', 교체함수, '문자열', 바꿀횟수)
    >>>
    >>> 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'
    >>>
    >>> # 교체 함수 내용이 간단하다면 람다 표현식을 만들어 넣어도 됨
    >>>
    >>> 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'

    'Python' 카테고리의 다른 글

    모듈과 패키지 만들기  (0) 2023.10.30
    모듈과 패키지 사용  (0) 2023.10.30
    데코레이터(장식자)  (0) 2023.10.25
    코루틴  (0) 2023.10.24
    제너레이터(발생자)  (0) 2023.10.24
Designed by Tistory.