티스토리 뷰

Python

Python,module re [정규표현식]

hwangyoungjae 2016. 4. 29. 13:10
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

정규표현식(Regular Expression)은 특정한 규칙을 가진 문자열을 표현하는데 사용되는 형식언어로서 주어진 패턴으로 문자열을 검색/치환하는데 주로 사용되며, vi같은 편집기나 sed, grep같은 프로그램에서 널리 사용됨

파이썬에서는 정규표현식을 위하여 re모듈에 문자열의 검색치환분리와 같은 기능을 제공함

 

-. 정규표현식 문법

특수 문자

의미

.

개행 문자를 제외한 1자를 나타냄

re.DOTALL이 설정되어 있으면개행을 포함한 문자1자를 나타냄

^

문자열의 시작을 나타냄

re.MULTILINE이 설정되어 있으면 매 라인마다 매치됨

$

문자열의 종료를 나타냄

re.MULTILINE이 설정되어 있으면 매 라인마다 매치됨

[]

문자의 집합을 나타냄예를 들어

[abcd]의 경우 'a', 'b', 'c', 'd'중 한 문자와 매치됨

또한 문자의 집합을 [a-d]로도 나타낼수있음

또한 [^5]와 같이 '^' []안에서 쓰이는 경우에는 5를 제외한 모든 문자를 나타냄

또한 [$]와 같이 '$' []안에서 쓰이는 경우는 순수하게 '$'문자를 나타냄

|

'A|B'와 같은 경우 'A' 혹은 'B'를 나타냄(OR연산)

()

괄호 안의 정규식을 구릅으로 만듬.

직접 '(', ')'(괄호)를 매칭시키기 위하여 '\(', '\)' '[(]', '[)]'로 나타냄

*

문자가 0회 이상 반복됨을 나타냄

+

문자가 1회 이상 반복됨을 나타냄

?

문자가 0회 혹은 1회 반복됨을 나타냄

{m}

문자가 m회 반복됨을 나타냄

{m,n}

문자가 m회부터 n회까지 반복되는 모든 경우를 나타냄

{m,}

문자가 m회부터 무한 반복되는 모든 경우를 나타냄

일반 문자와 특수문자의 조합으로 패턴을 만들어 문자열을 검색한다.

아래는 각 정규표현식에 대한 예이다.

정규식 'app.e' 'apple', 'appLe', 'app-e', 'app e'이 매치됨

정규식 '^app' 'apple and orange'는 매치되지만, 'orange and apple'는 매치되지 않는다.

정규식 'ple$' 'orange and apple'는 매치되지만 'apple and orange'는 매치되지 않는다.

정규식 'appl[a-z]' 'apple', 'applz'와 같이 가장 마지막에 소문자가 오는경우는 매치되지만 'applE', 'appl4', 'appl_', 'appl'와 같은 경우는 매치되지 않는다.

정규식 appl[^a-z]는 위의 경우와 반대로 마지막에 소문자가 오는 경우를 제외한 모든 경우에 매치됨

정규식 'apple|E' 'apple','applE'와 매치됨

정규식 'ap*le' 'ale', 'aple', 'appple'와 같이 p 0회이상 반복되는 모든 경우와 매치됨

정규식 'ap+le' 'aple','appple'와 매치되지만 'ale'는 매치되지 않는다.

정규식 'ap?le' 'ale', 'aple'와 매치되지만, 'apple', 'appple'와는 매치되지 않는다.

정규식 'ap{2}le' 'apple'만 매치되고, 'aple', 'aple', 'appple'등과는 매치되지 않는다.

정규식 'ap{2,4}le' 'apple', 'appple', 'apppple'와 매치되지만, 'ale', 'aple', 'appppple'등과는 매치되지 않는다.

정규식 'ap{2,}le' 'apple', 'apppppple'와 같이 p 2회이상 반복되는 모든 경우는 매치되지만, 'ale', 'aple'는 매치되지 않는다.

또한 확장문법으로 re모듈에서는 자주사용되는 문자열의 집합을 이스케이프 문자열로 미리 정의하고 있다.

종류

설명

\w

유니코드인 경우 숫자밑줄(underscore, '_')를 포함하는 모든 언어의 표현 가능한 문자,

아스키코드인 경우 '[a-zA-Z0-9_]'와 동일함

\W

유니코드인 경우 숫자밑줄과 표현 가능한 문자를 제외한 나머지 문자,

아스키코드인 경우 '[^a-zA-Z0-9_]'와 동일함

\d

유니코드인 경우 [0-9]를 포함하는 모든 숫자임.

아스키코드인 경우 [0-9]와 동일함

\D

유니코드인 경우 숫자를 제외한 모든 문자

아스키코드인경우 [^0-9]와 동일함

\s

유니코드인경우 [\t\n\r\f\v]를 포함하는 공백문자

아스키코드인경우 [\t\n\r\f\v]와 동일함

\S

유니코드인 경우 공백문자를 제외한 모든 문자

아스키코드인경우 [^\t\n\r\f\v]와 동일함

\b

단어의 시작과 끝의 빈공백

\B

단어의 시작과 끝이 아닌 빈공백

\\

역슬래시(\) 문자 자체를 의미

\[숫자]

지정된 숫자만큼 일치하는 문자열을 의미

\A

문자열의 시작

\Z

문자열의 끝

 

 

>> re모듈 함수 <<

re(Regular Expression)모듈에서 지원하는 함수는 아래와 같다공통적으로 사용된 pattern은 앞에서 설명한 정규표현식이 사용되며, string에는 검색대상 문자열이 온다.

함수명

설명

re.search(pattern, string[,flags])

string전체에 대해서 pattern이 존재하는지 검사하여 MatchObject인스턴스를 반환

re.match(pattern, string[,flags])

string시작부분부터 pattern이 존재하는지 검사하여 MatchObject인스턴스를 반환

re.split(pattern, string[,maxsplit=0])

pattern을 구분자로 string을 분리하여 리스트로 반환

re.findall(pattern, string[,flags])

string에서 pattern과 매치되는 모든 경우를 찾아 리스트로 반환

re.finditer(pattern, string[,flags])

string에서 pattern과 일치하는 결과에 대한 이터레이터 객체를 반환

re.sub(pattern, repl, string[,count])

string에서 pattern과 일치하는 부분에 대하여 repl로 교체하여 결과 문자열을 반환함

re.subn(pattern, repl, string[,count])

re.sub()함수와 동일하게 동작하나결과(결과문,매칭횟수)를 튜플로 반환

re.escape(string)

영문자숫자가 아닌 문자에 대하여 백슬러시 문자를 추가함.

메타문자를 포함한 문자열을 정규식으로 변경할수 있음.

re.compile(pattern,[flags])

pattern을 컴파일하여 '정규표현식 객체(regular expression object)'를 반환함

 

>re.search(), re.match()

re모듈에는 문자열 검색을 위한 re.search() re.match()함수가 있다두 함수는 공통된 특성이 많다두 함수 모두 첫인자로 찾고자 하는 패턴을두번째 인자로 검색대상 문자열을 받아들인다함수를 수행하면 Match객체가 반환되고만약 검색 결과 일치하는 문자열이 없는 경우두 함수 모두 None를 반환한다.

>>> import re

>>> re.match('[0-9]*th','35th'#결과로 Match객체를 반환

<_sre.SRE_Match object at 0x00FF62F8>

>>> re.search('[0-9]*th','35th')

<_sre.SRE_Match object at 0x00FF62F8>

두 함수의 다른점은 re.match()함수의 경우 대상 문자열의 시작부터 검색을 하지만, re.search()함수는 대상문자열 전체에 대해서 검색을 수행한다는 것이다예를 들어 아래와 같이 검색의 대상이 되는 문자열에 공백이 있는 경우나 키워드와 일치하는 문자열이 대상 문자열의 중간 이후에 존재하는 경우, re.match()함수는 검색하지 못하는것을 알수 있다.

>>> bool(re.match('[0-9]*th','      35th')) #불린으로 검색결과 확인 가능

False

>>> bool(re.search('[0-9]*th','      35th'))

True

>>> bool(re.match('ap','This is an apple')) #문자열의 시작부터 검색

False

>>> bool(re.search('ap','This is an apple')) #문자열 전체에 대하여 검색

True

 

※ 로 문자열 표기법(Raw string notation)

이스케이프 문자열을 표현하기 위하여 '\'(백슬래시문자를 사용하기 때문에문자'\'를 정규표현식으로 표현하기 위해서는 '\\\\'일반 문자열에서는 '\\'로 표현해야 한다그래서 '\apple'이란 문자열을 검색하기 위해서는 아래와 같이 매우 복잡한 형식으로 표현해야 한다.

>>> bool(re.search('\\\\\w+','\\apple'))

True

로 문자열 표기법은 문자열 앞에 'r'을 더한것으로 \(백슬래시)문자를 이스케이프 문자열로 처리하지 않고 일반문자와 동일하게 처리한다이렇게 함으로써 정규표현식 및 문자열에서 '\'를 간단하게 표현할수 있다일반적으로 정규표현식에 사용되는 문자열에서는 이러한 편리함때문에 많이 사용한다

>>> bool(re.search('\\\\\w+','\\apple')) #로 표기법이 적용안된경우

True

>>> bool(re.search(r'\\\w+',r'\apple')) #로 표기법이 적용된경우

True

 

>re.split()

다음은 대상 무자열을 입력된 패턴을 구분자로 하여 분리하는 예제이다.

str.split()메소드와 다르게 다양한 구분자처리도 가능하다.

또한 최대 분리횟수를 지정한 경우해당횟수만큼만 문자열을 분리한다.

>>> re.split('[:. ]+','apple orange:banana  tomato')

['apple', 'orange', 'banana', 'tomato']

>>> re.split('([:. ])+','apple orange:banana  tomato')

['apple', ' ', 'orange', ':', 'banana', ' ', 'tomato']

>>> re.split('[:. ]+','apple orange:banana  tomato',2)

['apple', 'orange', 'banana  tomato']

다음처럼 여러줄에 걸쳐서 작성된 문자열에 대하여 빈줄을 제외한 매 행을 리스트객체에 저장할수 있다.

>>> text='''hello python

python is powerful

good job'''

>>> text

'hello python\npython is powerful\ngood job'

>>> re.split('\n',text)

['hello python', 'python is powerful', 'good job']

 

>re.findall()

re.findall()함수는 검색 문자열에서 패턴과 매칭되는 모든 경우를 찾아 리스트로 반환한다.

만약 매치되는 문자열이 없는 경우빈리스트를 반환한다.

>>> re.findall(r"app\w*","application orange apple banana")

['application', 'apple'] #매치되는 문자열이 있는경우

>>> re.findall(r"king\w*","application orange apple banana")

[] #매치되는 문자열이 없는경우

 

>re.sub()

패턴과 일치하는 문자열을 변경할때에는 re.sub()함수를 이용한다.

첫인자로 매칭할 패턴을두번째 인자로 변경할 문자열세번째 인자로 검색대상문자열을 입력한다.

아래의 예제는 주민등록번호 문자열에 대해서 '-'를 제거하는 경우와 각 문자열 필드의 구분자를 하나로 통일시키는 경우이다.

>>> re.sub('-','','123456-1234567')

'1234561234567' #주민등록번호 형식변경

>>> re.sub(r'[:,|\s]','_','apple:orange banana|tomato')

'apple_orange_banana_tomato' #필드구분자를 하나로 통일

또한 아래와 같이 문자열의 변경횟수를 제한할수도 있음

>>> re.sub(r'[:,|\s]','_','apple:orange banana|tomato',2)

'apple_orange_banana|tomato'

또한 변경할 문자열에 대해서 매칭된 문자열을 사용할수도 있음

정규표현식에서 매칭시킬 패턴 중 변경할 문자열에 사용할 부분에 대해서 소괄호로 감싸주고변경할 문자열에 대해서는 '\<숫자>'형태로 사용할수 있음만약 매칭시킬 패턴에 명시적으로 이름을 지정('(?P<패턴_이름>)')하면변경할 문자열에서도 그 이름을 '\g<패턴_이름>'형태로 사용할수 있다.

아래는 html문장의 일부분 중 연도부분을 이탤릭체로 변경하는 예이다.

>>> re.sub(r"\b(\d{4}-\d{4})\b",r"<I>\1</I>","Copyright derick 1990-2009")

'Copyright derick <I>1990-2009</I>' #결과집합의 인덱스를 사용하는 경우

>>> re.sub(r"\b(?P<year>\d{4}-\d{4})\b",r"<I>\g<year></I>","Copyright Derick 1990-2009")

'Copyright Derick <I>1990-2009</I>' #지정한 이름을 사용하는 경우

아래는 변경할 문자열을 명시적으로 입력하는 대신에 함수로 변경작업을 수행하는 예제임

두번째 인자인 변경할 문자열 대신에 미리 정의한 함수의 이름을 인자로 전달함

>>> def Upper(m):

             return m.group().upper()

>>> re.sub("[T|t]he",Upper,"The time is the money")

'THE time is THE money'

 

>> 정규표현식객체 <<

동일한 패턴을 연석족으로 검색하는 경우에도지금까지 설명된 함수들을 정규식으로 표현된 문장을 매번 다시 분석하여 검색함.

>>> re.findall(r"app\w*","application orage apple banana")

['application', 'apple']

>>> re.findall(r"app\w*","There are so many apples in the basket")

['apples']

 

이러한 비 효율적인 동작을 피하고자 정규표현식을 컴파일하여 정규표현식 객체를 생성할수 있음.

원형은 아래와 같다.

re.compile(pattern[,flags])

 

이렇게 생성된 정규표현식 객체는 re모듈에서 지원하던 모든 검색함수를 정규식의 분석없이 검색할수 있기 때문에동일 패턴을 반복적으로 검색하는 경우 성능향상을 기대할수 있다.

>>> c=re.compile(r"app\w*")

>>> c.findall("application orage apple banana")

['application', 'apple']

>>> c.findall("There are so many apples in the basket")

['apples']

 

다음의 표는 정규표현식 객체가 지원하는 메소드로 re모듈의 검색함수와 이름이 동일하다는 것을 알수 있다.

다른 점이 있다면 정규식이 이미 객체에 컴파일되어 있기 때문에, re모듈과 다르게 정규식을 인자로 전달할 필요가 없다.

메소드

설명

search(string[,pos[,endpos]])

string전체에 대해서 컴파일된 패턴이 존재하는지 검사하여

MatchObject인스턴스를 반환

match(string[,pos[,endpos]])

string에 시작부분부터 컴파일된 패턴이 존재하는지 검사하여

MatchObject인스턴스를 반환

split(string[,maxsplit=0])

컴파일된 패턴을 구분자로 string을 분리하여

리스트로 반환

findall(string[,pos[,endpos]])

string에서 컴파일된 패턴과 매치되는 모든 경우를 찾아

리스트로 반환

finditer(string[,pos[,endpos]])

string에서 컴파일된 패턴과 일치하는 결과에 대한

이터레이터 객체를 반환

sub(repl,string[,count=0])

string에서 컴파일된 패턴과 일치하는 부분에 대하여

repl로 교체하여 결과 문자열을 반환

subn(repl,string[,count=0])

re.sub()함수와 동일하게 동작하나,

결과(결과문자열,매칭횟수)를 튜플로 반환

 

또한 이렇게 compile()함수를 이용하여 정규식을 분석하는 경우추가적으로 몇가지 옵션플래그를 사용할수 있다.

플래스

설명

re.I(대문자 아이)

re.IGNORECASE

대소문자를 구분하지 않고 매칭작업을 수행

re.M

re.MULTILINE

문자열이 여러줄인경우이 플래그가 설정되면

'^'는 각 행의 처음을 나타내고

'$'는 각 행의 마지막을 나타냄

기본값은 '^'는 문자열의 첫행의 처음을,

'$'는 마지막행의 마지막만을 나타냄

re.S

re.DOTALL

이 플래그가 설정되면 개행 문자도 '.'으로 매칭됨

기본값은 '.'은 개행문자와 매칭되지 않는다.

re.X

re.VERBOSE

정규표현식을 보기 쉽게 작성할수 있다.

정규표현식에서 '#'으로 시작하는 파이썬 주석과 빈 공백은 무시된다.

빈 공백을 표현하기 위해서는 '\ '으로 표현해야 한다.

re.A

re.ASCII

이 플래그가 설정되면 '\w, '\W', '\b', '\B', '\s', '\S'

는 유니코드대신에 아스키코드만 매칭됨

re.L

re.LOCALE

이 플래그가 설정되면 '\w, '\W', '\b', '\B', '\s', '\S' 는 현재 로케일 설정을 따른다.

파이썬3버전부터 기본적으로 유니코드가 사용되기 때문에 자주 사용되지는 않는다.

 

>>re.I 예제

아래는 문자열 검색시 re.I를 설정하여 대소문자를 구분하지 않는 예제이다.

>>> s='Apple is a big company and apple is very delicious.'

>>> c=re.compile('apple'#대소문자를 구분

>>> c.findall(s)

['apple']

>>> c=re.compile('apple',re.I) #대소문자를 구분하지 않음

>>> c.findall(s)

['Apple', 'apple']

 

>>re.M 예제

아래는 re.M을 설정하여 빈 라인을 제외하고 라인별로 분리하는 예제이다.

>>> s='''abc def ghi

jklmn

 

opqrstu'''

>>> c=re.compile('^.+'#첫라인만 매칭

>>> c.findall(s)

['abc def ghi']

>>> c=re.compile('^.+',re.M) #MULTILINE이 설정된 경우

>>> c.findall(s)

['abc def ghi', 'jklmn', 'opqrstu']

 

>>re.X 예제

아래는 웹에 있는 html코드에서 title태그의 내용을 출력하는 예제이다.

아래와 같이 정규표현식 자체에 주석이 없으면 다른 사람이 이해하기도 힘들고추후 유지보수도 어렵다.

>>> import urllib.request, re

>>> web=urllib.request.urlopen('http://www.python.org')

>>> html=web.read() #html 코드내용 저장

>>> web.close()

>>> code=str(html).encode('utf-8').decode('cp949'#윈도우 형식의 문자열로 변경

>>> c=re.compile(r'.*?<title.*?>(.*)</title>',re.I|re.S) #두개의 플래그 설정

>>> c.findall(code)

['Python Programming Language &ndash; Official Website']

 

아래와 같이 re.X를 설정하여 정규표현식 자체에 개행공백을 입력할수 있으며해당정규식에 대한 주석도 입력이 가능함

>>> import urllib.request, re

>>> web=urllib.request.urlopen('http://www.python.org')

>>> html=web.read()

>>> web.close()

>>> s=str(html).encode('utf-8').decode('cp949')

>>> c=re.compile(r'''.*?   #<title>앞의 모든 문자 무시

<title.*?>                 #<title>태그옵션은 무시

(.*)                      #내용을 저장

</title>                   #</title>태그

''', re.I|re.S|re.X)         #대소문자 무시개행포함

>>> c.findall(s)

['Python Programming Language &ndash; Official Website']

 

>> Match객체 <<

Match객체는 match(), search()의 수행결과로 생성되며검색된 결과를 효율적으로 처리할수 있는 기능을 제공함.

Match객체가 지원하는 메소드와 속성은 아래와 같다.

메소드

내용

group([group1,...])

입력받은 인덱스에 해당하는 매칭된 문자열 결과의 부분 집합을 반환

인덱스가 '0'이거나 입력되지 않은 경우 전체 매칭문자열을 반환

groups()

매칭된 결과를 튜플형태로 반환

groupdict()

이름이 붙어진 매칭결과를 사전형태로 반환

start([group])

매칭된 결과 문자열의 시작 인덱스를 반환

인자로 부분 집합의 번호나 명시된 이름이 전달된 경우,

그에 해당하는 시작 인덱스를 반환

end([group])

매칭된 결과 문자열의 종료 인덱스를 반환

인자로 부분 집합의 번호나 명시된 이름이 전달된 경우,

그에 해당하는 종료인덱스를 반환

 

속성

내용

pos

원본 문자열에서 검색을 시작하는 위치

endpos

원본 문자열에서 검색을 종료하는 위치

lastindex

매칭된 결과 집합에서 마지막 인덱스 번호를 반환

일치된 결과가 없는 경우 None을 반환

lastgroup

매칭된 결과 집합에서 마지막으로 일치한 이름을 반환

정규식의 매칭조건에 이름이 지정되지 않았거나

일치된 결과가 없는 경우에는 None을 반환

string

매칭의 대상이 되는 원본문자열

 

Match객체는 search(), match()의 수행결과로 반환되며검색결과에 따라 내부적으로 불린값을 가지고 있다.

또한 검색결과를 효율적으로 분석할수 있는 다양한 메소드를 지원한다.

아래는 일반적인 형식의 전화번호를 인식하여 Match객체가 지원하는 메소드로 분석하는 예이다.

일반적인 전화번호의 형식은 '지역번호 - 국번 - 고객번호'의 형식으로 고객번호는 4자리고정인 반면 지역번호는 2자리~3자리국번은 3자리~4자리가 될수 있다.(ex : 02-123-4567, 032-1234-5678)

>>> import re

>>> telcheck=re.compile(r'(\d{2,3})-(\d{3,4})-(\d{4})')

>>> bool(telcheck.match('02-123-4567'))

True

>>> bool(telcheck.match('02-123-4567'))

False

>>> bool(telcheck.match('3402-123-4567'))

False

>>> bool(telcheck.match('032-123-4567'))

True

 

이렇게 검색된 결과는 group(), groups()로 매칭된 내용을 확인할수 있으며, start(), end()로 원본 문자열에서 매칭된 위치를 확인할수 있다.

매칭되는 순서대로 인덱스 번호가 주어지며이 번호는 메소드의 인자로 전달될수 있다.

인자가 주어지지 않거나 '0'인 경우 전체 검색 문자열을 나타낸다.

>>> m=telcheck.match('02-123-4567')

>>> m.groups() #매칭된 문자열 집합을 튜플로 반환

('02', '123', '4567')

>>> m.group() #매칭된 전체 문자열을 반환

'02-123-4567'

>>> m.group(2,3) #두번째와 세번째 매칭된 문자열을 튜플로 반환

('123', '4567')

>>> m.start() #매칭된 전체 문자열의 시작 인덱스

0

>>> m.end() #매칭된 전체 문자열의 종료 인덱스

11

>>> m.start(2) #두번째 매칭된 문자열("123")의 시작 인덱스

3

>>> m.end(2) #두번째 매칭된 문자열("123")의 종료 인덱스

6

>>> m.string[m.start(2):m.end(3)] #지역번호를 제외한 전화번호만 출력

'123-4567'

 

정규식 작성시 '(?<이름>..)'형식으로 매칭결과에 대해서 이름을 부여할수 있다.

이렇게 이름이 부여된 경우에는 각 함수의 인자로 지정된 이름을 인덱스 번호대신 전달할수 있다.

또한 groupdict()메소드를 이용하여 사전형태로 이름과 검색된 문자쌍을 얻을수 있다.

>>> m=re.match(r"(?P<area_code>\d+)-(?P<exchange_number>\d+)

-(?P<user_number>\d+)","02-123-4567"#매칭되는 문자열에 이름을 부여

>>> m.group('user_number') #'user_number'의 이름에 해당하는 문자열

'4567'

>>> m.start('user_number'#'user_number'의 이름에 해당하는 시작인덱스

7

>>> m.groupdict() #검색된 모든 문자열에 대해 사전형태로 반환

{'area_code': '02', 'user_number': '4567', 'exchange_number': '123'}

 

 

참조 : 빠르게 활용하는 파이썬프로그래밍

'Python' 카테고리의 다른 글

Python, @property  (0) 2016.05.09
Python,module copy [객체복사]  (0) 2016.04.29
Python, 예외처리  (0) 2016.04.29
Python, 파일 입출력  (0) 2016.04.29
Python,module glob  (0) 2016.04.29
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함