전체 페이지뷰

2016년 12월 1일 목요일

Regular expression with Python, Searching

Searching

RegexObject의 중요한 메소드들을 알아보겠습니다. 다른 언어들이 match만 있는 반면에 파이썬에는 match와 search의 두 가지 operation이 있음을 주목하시기 바랍니다.


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

이 메소드는 문자열 시작부분에서 패턴매칭을 시도합니다. 매칭되는 결과로 MatchObject를 리턴합니다. <HTML>이라는 문자열이 있는가를 매칭하는 예를 들어 보겠습니다.

>>> pattern = re.compile(r'<HTML>')
>>> pattern.match("<HTML><head>")
<_sre.SRE_Match object; span=(0, 6), match='<HTML>'>

패턴을 컴파일하여 문자열에서 match를 시도하여 매치되는 결과를 얻었습니다.

문자열이 <HTML>로 시작하지 않으면 어떻게 되는지 봅시다.
(다시 말하지만 ⇢는 공백입니다.)

>>> pattern.match("⇢<HTML>")

반환되는 결과가 없습니다. 처음이 공백이라 match되지 않기 때문입니다.
같은 방식으로 이번에는 search를 사용해 봅시다.

>>> pattern.search("<HTML>")
<_sre.SRE_Match object; span=(1, 7), match='<HTML>'>

이번에는 매칭되는 결과가 있습니다.

match 메소드에 위치 인자를 줄 수도 있습니다.
>>> pattern.match("⇢⇢<HTML>")
None

>>> pattern.match("⇢⇢<HTML>", 2)
<_sre.SRE_Match object; span=(2, 8), match='<HTML>'>

공백이 두 개 있을 때, 위치 인자가 없으면 결과를 반환하지 않지만,
2라고 위치 인지를 주었을 때는 결과를 반환함을 알 수 있습니다.

그러나, 포지션 인자를 주었다고 해서 그 문자열이 그 포지션에서 시작된다는 뜻은 아닙니다. 다음의 예를 봅시다.

>>> pattern = re.compile(r'^HTML')

>>> pattern.match("<HTML>")
<_sre.SRE_Match object; span=(0, 6), match='<HTML>'>

>>> pattern.match("<HTML>", 2)
None

이번에는 표현식에 '^'를 추가하여 문장의 시작점에서 찾으라는 옵션을 주었습니다.
그래서 "<HTML>"은 반환값이 있지만,
공백을 앞에 두개 둔 "⇢<HTML>"은 비록 포지션 2에서 시작하라는 옵션을 주었어도 그 문장의 시작점에 존재하는 문자열이 아니므로 반환값이 없습니다.

그럼 이 경우에는 어떻게 될까요?
>>> pattern.match("<HTML>"[2:])
<_sre.SRE_Match object; span=(0, 6), match='<HTML>'>

슬라이싱은 문자열을 새로 만듭니다. 따라서 반환값이 존재합니다. pos 인자는 단지 매칭되는 위치를 지정하는 것에 불과합니다.

두번째 인자인 endpos는 어떨까요?

>>> pattern = re.compile(r'<HTML>')

>>> pattern.match("<HTML>"[:2])
>>> pattern.match("<HTML>", 0, 2)

두 경우 공히 반환값이 없습니다. 첫 인자였던 pos와는 다릅니다.
그럼 $값을 주어 문장의 끝에서 찾게 하면 어떻게 될까요?

>>> pattern = re.compile(r'<HTML>$')

>>> pattern.match("<HTML> ", 0,6)
<_sre.SRE_Match object; span=(0, 6), match='<HTML>'>

>>> pattern.match("<HTML> "[:6])
<_sre.SRE_Match object; span=(0, 6), match='<HTML>'>

두 경우 모두 반환값이 존재합니다. 따라서 endpos는 slicing하여 새로운 문자열로 만든 것과 마찬가지 결과를 가집니다.


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

이 메소드는 다른 언어의 match와 비슷합니다.어느 위치에서부터든 패턴 매칭을 시작하여 일치하는 결과가 있으면 MatchObject를 반환합니다.

>>> pattern = re.compile(r"world")

>>> pattern.search("helloworld")
<_sre.SRE_Match object; span=(6, 11), match='world'>

>>> pattern.search("holamundo ")

pos와 endpos는 match에서와 같은 의미로 쓰입니다.
컴파일 시 MULTILINE 플래그를 써서 여러 줄을 search하는 경우를 봅시다..


>>> pattern = re.compile(r'^<HTML>', re.MULTILINE)



>>> pattern.search("<HTML>")

<_sre.SRE_Match object; span=(0, 6), match='<HTML>'>

>>> pattern.search("⇢<HTML>")
None

>>> pattern.search("⇢⇢\n<HTML>")
<_sre.SRE_Match object; span=(3, 9), match='<HTML>'>


여러 줄로 된 텍스트의 각 줄 맨 앞에서 처음 나오는 <HTML>이라는 문자열을 찾으라는 뜻이 됩니다.

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

앞의 메소드들과 달리 findall은 매칭되는 결과들을 찾아 MatchObject가 아닌 리스트로 반환합니다.

>>> pattern = re.compile(r"\w+")
>>> pattern.findall("hello world")
['hello', 'world']

그럼 이 경우는 어떨까요?
>>> pattern = re.compile(r'a*')
>>> pattern.findall("aba")
['a', '', 'a', '']

헛갈립니다.  *은 선행 리터럴인 a가 0번 이상 만복되는 것을 찾으라는 건데 결과로 a뿐만 아니라 공백 ''도 두번이나 들어있습니다.
*을 ?로 바꿔도 같은 결과가 나옵니다.
>>> pattern = re.compile(r'a?')
>>> pattern.findall("aba")
['a', '', 'a', '']

+(한번 이상 반복)로 바꾸면 얘기가 다릅니다.
>>> pattern = re.compile(r'a+')
>>> pattern.findall("aba")
['a', 'a']

이것은 *, ? 두 경우 모두 찾지 못하는 경우도 포함하기 때문입니다.

그리고, 우리가 문자열에 마지막을 뜻하는 $표시를 굳이 넣지 않더라도 엔진 레벨에선 문자열의 끝에 $가 있는 것으로 처리합니다. aba를 검색했지만 aba$가 검색된 것은 그 때문입니다.

패턴을 그룹핑하여 검색하면 어떻게 될까요?

>>> pattern = re.compile(r"(\w+)⇢(\w+)")
>>> pattern.findall("Hello world hola mundo")
[('Hello', 'world'), ('hola', 'mundo')]

튜플의 리스트로서 반환됩니다.

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

findall과 기본적으로 같은 메소드이지만 반환되는 결과가 튜플의 리스트가 아닌 iterator입니다. 그리고 그 iterator의 요소 각각은 MatchObject입니다. 그래서 각각의 요소를 처리하기 수월해집니다.

위에 사용했던 패턴을 그대로 둔채 이번에는 findall이 아닌 finditer를 써서 내부적으로 어떤 일이 일어나는가를 살펴 보도록 합시다.

>>> pattern = re.compile(r"(\w+)⇢(\w+)")
>>> it = pattern.finditer("Hello world hola mundo")
>>> match = next(it)
>>> match.groups()
('Hello', 'world')
>>> match.span()
(0, 11)
>>> match = next(it)
>>> match.groups()
('hola', 'mundo')
>>> match.span()
(12, 22)

위와 같이 각각의 요소에 대해 iterate 할수 있으며, 위치도 반환받을 수 있습니다.


댓글 없음:

댓글 쓰기