728x90

이번 시간부터는 python 에서 정규식을 사용하여 문자열 검색을 하는 방법을 실습해보겠습니다.

>>> import re

>>> dir(re)

['DEBUG', 'DOTALL', 'I', 'IGNORECASE', 'L', 'LOCALE', 'M', 'MULTILINE', 'S', 'Scanner', 'T', 'TEMPLATE', 'U', 'UNICODE', 'VERBOSE', 'X', '_MAXCACHE', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__version__', '_alphanum', '_cache', '_cache_repl', '_compile', '_compile_repl', '_expand', '_pattern_type', '_pickle', '_subx', 'compile', 'copy_reg', 'error', 'escape', 'findall', 'finditer', 'match', 'purge', 'search', 'split', 'sre_compile', 'sre_parse', 'sub', 'subn', 'sys', 'template']

>>> 

re 모듈에서 제공하는 함수 중에서 가장 많이 사용하는 3 가지를 이용해서 실습을 해보겠습니다.

  1. search : string 전체를 검색하여 match 되는 첫번째 문자열을 찾음.
      <match object> = re.search( pattern, string, flag=0)
  2. match : string 의 첫 문자열만을 비교하여 pattern 과 match 되는지를 확인.
      <match object> = re.match( pattern, string, flag=0)
  3. findall : string 전체에서 pattern 과 일치하는 것을 모두 찾아서 list 로 리턴해줌.
      [list of groups, list of tuples] = re.findall( pattern, string, flag=0 )

위의 3 함수 모두 인자의 형태는 같습니다. 정규식을 나타내는 pattern 과 원본 string, 그리고 option flag 입니다.
중요한 것은 리턴형인데 이 리턴형을 정확히 알고 있어야만 해당 결과를 이용하여 또 다른 작업을 할 수 있기 때문입니다. 예를 들어 re.search 의 리턴형이 match object 라는 것을 모른 채 마치 string 처럼 취급해서 연산을 하려고 하면 계속해서 오류를 만날겁니다.
match object 는 객체이니만큼 당연히 해당 객체가 제공하는 함수들을 이용하여 연산 및 작업을 해야 합니다.

먼저 간단히 re.search 를 이용해서 다양한 문자열 검색을 시험해볼까요 ?
첫번째 인자로 주어지는 pattern 에는 이전 시간에 배운 정규식 표현들이 사용될 수 있습니다. 정규식 표현들은 여러분이 머리속에 많이 알고 있을수록 상황에 맞게 좀더 예쁜 pattern 을 만들어낼 수 있습니다.
사실 저는 머리가 나빠서 몇 가지밖에 기억하지 못하고 나머지는 찾아보는 편입니다. ^^

>>> import re                                              # re 모듈 import

>>> match = re.search('iig', 'called piig');     # search 를 이용하여 iig 문자열 검색
>>> match.group()                                      # search 결과 match object 의 group 함수 이용
'iig'
>>> type(match)                                         # search 의 return 형이 match object 임
<type '_sre.SRE_Match'>
>>> match
<_sre.SRE_Match object at 0x2b26d13dcbf8>

>>> match = re.search('igs', 'called piig');    # 없는 문자열 검색
>>> match.group()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
>>> type(match)                                         # 검색 후 return 형을 확인해보니 NoneType
<type 'NoneType'>                                      # 이는 검색 결과가 없다는 것임.

>>> match = re.search('..g', 'called piig');     # . 은 하나의 문자를 가리킴. 즉 앞에 아무문자나
>>> match.group()                                      # 두문자가 온 후 그 다음 문자가  g 인 것을 검색
'iig'

>>> match = re.search('i..g', 'called piig');    # i 와 g 사이에 문자 두개가 있는 것을 검색
>>> match.group()                                      # 결과가 없는 것임. NoneType
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'

>>> match = re.search('p..g', 'called piig');           # p 와 g 사이 문자 두개
>>> match.group()
'piig'

>>> match = re.search('a.c', 'a.c called piig');      # a 와 c 사이에 한문자가 있는 문자열 검색
>>> match.group()
'a.c'

>>> match = re.search('a\.c', 'a.c called piig');   # a 와 c 사이 .(점) 문자가 있는 문자열 검색
>>> match.group()
'a.c'

>>> match = re.search('a.c', 'abc called piig');     
# a 와 c 사이에 한문자가 있는 문자열 검색
>>> match.group()
'abc'

>>> match = re.search('a\.c', 'abc called piig');   # a.c 라는 문자열 검색 (\. 은 . 문자)
>>> match.group()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'

>>> match = re.search(':\w\w', 'abc called :piig');  # : 문자 뒤에 두개의 아무문자나
>>> match.group()
':pi'

>>> match = re.search(':[\w]+', 'abc called :piig');  # : 문자뒤에 아무문자나 1 개 이상 붙은것
>>> match.group()
':piig'

위의 실습에서 match.group() 이라는 함수를 수행하면 'iig' 라는 문자열을 반환하는 예를 보셨을 겁니다.
그런데, 이런 문자열을 반환하는 함수 이름을 왜 group() 이라고 지었을까요? 궁금하지 않으세요 ? 전 처음에는 그게 궁금하더라구요.
그래서, 좀 찾아봤더니 search 나 match 을 이용하여 검색할 때 첫번째 인자인 pattern 을 여러개를 줄 수 있어서, 결과도 하나의 string 이 아닌 group(tuple) 으로 리턴할 수 있기 때문에 그렇습니다.

다음의 예를 한번 보시죠.
pattern 을 하나하나 소괄호'()'로 묶어서 search 의 인자로 제공했습니다. 첫번째 검색 pattern 은 (\d\d\d) 이고 두번째 검색 pattern 은 (\w\w\w)입니다.
이렇게 검색하려는 pattern 을 group 으로 한번에 여러 개를 제공한 것입니다.
그러니, 결과도 group 으로 나와야 겠지요. 즉, tuple 로 결과를 묶어서 리턴합니다.

>>> match = re.search('(\d\d\d)-(\w\w\w)', '123-abc');  # 2 개의 pattern group 검색
>>> match.groups()                               # 결과로 리턴된 group 을 tuple 로 묶어서 보여줌.
('123', 'abc')
>>> match.group()                                 # 결과 리턴된 각 group 의 string 을 연결해서 보여줌
'123-abc'
>>> match.group(0)                                # 0 번째 group 은 없고 match.group() 과 같은 결과
'123-abc'
>>> match.group(1)                                # 첫번째 group (element)을 string 으로 리턴
'123'
>>> match.group(2)                                # 두번째 group (element)을 string 으로 리턴
'abc'

>>> match = re.search('(ab)','ab');          # 하나의 pattern group 만으로 검색
>>> match.groups()                                # tuple 의 element 는 하나임
('ab',)
>>> match.group(1)
'ab'
>>> match.group(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: no such group

>>> match = re.search('(aaa).(bbb)@(cc.)', '  aaa.bbb@ccx');  # 3 개의 group 검색
>>> match.groups()
('aaa', 'bbb', 'ccx')

 

정규식도 배웠는데 조금만 더 복잡한 것을 시험해볼까요 ?

>>> m = re.search('([\w.]+)@([\w.]+)', 'blah dp.lee.dp.lee@gmail.com yyxdf @ aa@xx')
>>> m.groups()
('dp.lee.dp.lee', 'gmail.com')
>>> m.group(1)
'dp.lee.dp.lee'
>>> m.group(2)
'gmail.com'

위에서는 두 개의 group pattern 을 제공했는데, 첫번째와 두번째 모두 ([\w.]+) 형태로 찾는 것이네요.
이 정규식의 의미는 http://kio.zc.bz/Lecture/regexp.html ] 에서 다시 좀 찾아보신 후 의미를 파악해 주세요.

이상 search() 함수를 통해 문자열 검색을 시험해 보았는데요.
match() 함수는 pattern 문자열을 검색 대상 문자열의 맨 첫 문자열과 비교하여 pattern 이 일치하는지를 검사합니다. 일치하지 않으면 NoneType 리턴입니다.
이 점만 search() 함수와 다르고 나머지는 모두 같습니다.

>>> import re
>>> bool(re.match('[0-9]', '    77'))                     # 첫 문자가 안맞으면 false
False
>>> bool(re.search('[0-9]', '    77'))                   # 문자열 전체 검색이므로 있으면 true
True

findall() 함수는 search, match 함수와는 다르게 모든 문자열을 검색하여 match 되는 모든 것을 List 로 리턴한다는 점이 다릅니다.

다음의 예를 보실까요 ?

>>> m = re.findall('([\w.]+)@([\w.]+)', 'blah dp.leedp.lee@gmail.com yyxdf @ aa.xx@xx.net')
>>> m.groups()                                                 # 결과는 match object 가 아니기 때문에
Traceback (most recent call last):                        # groups 나 group 같은 함수가 없음.
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'groups'
>>> m.group()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'group'

>>> m
[('dp.leedp.lee', 'gmail.com'), ('aa.xx', 'xx.net')]  # 결과는 각 pattern group 결과(tuple)가
                                                                         # List 로 묶여진 것임.
                                                                         # 이후 m 은 List 연산들을 자유롭게 수행.

이제 정규식을 이용하여 문자열 검색을 수행하는 방법을 거의 알아본 것 같습니다.
이번 강의는 좀 길었네요.

그런데, 생각해보니 아직 한가지 알려드리지 않은 것이 또 남아있었네요. ㅎㅎ
바로 위의 3 함수의 세번째 인자인 option flag 에 대해 언급해 드리지를 않았네요.

option flag 는 여러분이 부여하지 않아도 default 가 지정되어 있습니다.
하지만, 특수한 경우에는 대소문자 구분을 해서 찾아라... 등 option 을 주면 편리한 경우들이 있습니다.
사용예를 하나만 보여드릴께요. flag 를 여러개 조합해서 부여할 때는 '|' 를 사용하면 됩니다.
즉, re.I | re.M | re.S 이런 방식으로 사용하시면 됩니다.

 

>>> m = re.search('AA', '  aa bb cc')                    # default option 으로 사용하면
>>> m.group()                                                     # 대소문자 구분해서 아무것도 못찾음
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'

>>> m = re.search('AA', '  aa bb cc', re.I)            # re.I 옵션을 사용하니 대소문자 미구분
>>> m.group()                                                    # case-insensitive 라고 하죠.
'aa'

아래의 표는 사용할 수 있는 option flag 에 대한 간단한 영문 설명입니다. 해석은 각자의 몫으로... ^^
사용할 수 있는 option flag 의 list 를 보고 싶으시면 dir(re)를 하시면 symbol list 가 나올텐데 유심히 보시면 아래의 목록이 모두 나올 것입니다.
항상 책이나 인터넷을 찾기 전에 dir, help 를 애용하는 것 잊지 마세요. ^^

 

Modifier Description
re.I Performs case-insensitive matching.
re.L Interprets words according to the current locale.This interpretation affects the alphabetic group (\w and \W), as well as word boundary behavior (\b and \B).
re.M Makes $ match the end of a line (not just the end of the string) and makes ^ match the start of any line (not just the start of the string).
re.S Makes a period (dot) match any character, including a newline.
re.U Interprets letters according to the Unicode character set. This flag affects the behavior of \w, \W, \b, \B.
re.X Permits "cuter" regular expression syntax. It ignores whitespace (except inside a set [] or when escaped by a backslash), and treats unescaped # as a comment marker.

 

출처 : http://cloudrain21.tistory.com/entry/파이썬python-정규식-정규표현식Regular-Expression-두번째

728x90

'Python' 카테고리의 다른 글

python basic  (0) 2014.05.05
python module  (0) 2014.05.02
python regular expressions  (0) 2014.05.02
python file  (0) 2014.05.02
python dictionary  (0) 2014.05.02

+ Recent posts