본문 바로가기
컴퓨터/디버깅

정규표현식이 들어간 디버깅

by adnoctum 2011. 2. 24.


   정규표현식을 이용하여 작업을 하려고 할 때, 원하는 패턴으로 matching 을 시켜서 일치된 패턴을 추출하여 사용하려 할 때가 자주 있다. 문제는 일치된 결과가 하나도 없을 때 왜 패턴 matching 이 되지 않았는지 알기 어렵다는 것인데, 다음과 같이 점진적으로 패턴을 지워 나가거나, 추가해 나가는 식으로 하면 왜 자신이 작성한 정규표현식이 주어진 텍스트에 맞지 않았는지에 대한 약간의 실마리를 얻을 수 있다. (아래는 파이썬에서의 사용 예. 펄이나 C++(boost에서), vim 도 비슷하게 할 수 있다)

   지금 내가 만난 문제는 이렇다. 일단 원본 텍스트가 다음과 같다.

uniprotkb:P47068|intact:EBI-3437

이 상황에서 위의 붉은 색으로 표시한 문자열(실제로는 db 의 ID)을 가져 오고자 한다. 그렇다면 이전에 설명했던 최소일치를 이용해 다음과 같이 할 수 있을 것이다.

m = re.search('uniprotkb:(.*?)|', line);

문제는 위처럼 하고 m.groups() 값을 출력해 보면 아무 것도 없다. 이럴 땐 pattern 의 일부를 지워 보면서 어떻게 match가 되었는지 보거나, 아니면 반대로 가장 단순한 pattern 에서 원래의 pattern 으로 만들어 가면서 어떻게 match 가 되었는지 살펴 본다. 즉,
'uniprotkb:(.*?)|' -> 'uniprotkb:(.*?)' -> 'uniprotkb:' -> ... : pattern 을 계속 지워 나간다.
'uniprotkb:' -> 'uniprotkb:(.*?)' -> 'uniprotkb:(.*?)|' -> ... : pattern 을 계속 채워 나간다.

왜 이렇게 하느냐 하면, 내가 준 pattern 이 잘못된 경우, 일단 맞는 곳부터 확인을 해 나가면 어느 pattern 이 추가되었을 때 match 가 깨지는 지 알 수 있기 때문이다. 지금의 경우 원래의 pattern 에서 일부를 지우면서 계속 확인해 나가는 방법을 택했다. 실제로 작업했던 순서는 다음과 같다.

[adnoctum@bioism ~]$ python
Python 2.4.3 (#1, Sep  3 2009, 15:37:12)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-46)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import re
>>> a = 'uniprotkb:P47068|intact:EBI-3437'
>>> m = re.search('uniprotkb:(.*?)|', a)                            --------- (1)
>>> m.groups()
('',)
>>> m = re.search('uniprotkb:(.*?)', a)                 ---------(2)
>>> m.groups()
('',)
>>> m = re.search('uniprotkb:(.*)', a)                  ----------(3)
>>> m.groups()
('P47068|intact:EBI-3437',)
>>> m = re.search('uniprotkb:(.*?)\|', a)               ----------(4)
>>> m.groups()
('P47068',)
>>>

우선 제일 먼저, 정말로 안 되었던 것인지 확인하기 위해 (1)번처럼 원래의 패턴을 이용해서 시도. match 된 부분을 저장하기 위해 패턴에 ( ) 를 사용한 후, 그러한 부분을 모두 뽑기 위해 m.groups() 를 했는데 결과가 비어 있다. 즉, 아무 것도 match 된 것이 없었던 것이다. 그래서 일단 한 글자를 지우고 (2)번처럼 시도. 그러나 희안하게 아무 것도 match 되지 않았다. (3) 번처럼 다시 시도. 그랬더니 이번엔 match 가 되었다. 여기까지 하고 생각해 볼 수 있는 것은,
(2) 의 경우 최소일치를 하기 위해선 패턴의 "끝"을 표시해 줄 수 있어야 하는데, (2) 의 경우 이것이 없어서 안 된 것 같다.
(3) 의 경우 uniprotkb: 뒤에 나오는 모든 문자열을 일치시키도록 했기 때문에 별 무리 없이 된다.
그렇다면 왜 (1)이 안 되었을까? 결국 최소일치에 사용될 "끝"을 제대로 지정해 주지 않았기 때문이겠지. 그런데 나는 | 를 끝으로 지정을 해 주었는데? 그렇다면 아마도 | 는 제대로 된 것이 아니고, 생각할 수 있는 것은 으레 그렇듯, | 는 특수 문자이겠지. 따라서 특수 문자인 | 대신 정말로 | 그 자체라는 것을 표시해 주기 위해 \| 로 해 보았다, (4) 번처럼. 역시나. 해결.

문득, 정규표현식에서 여러 글자를 묶은 여러 그룹에 대한 일치를 지정하기 위해 | 를 사용할 수 있다는 생각이 나는군.



ps. 이건 정말로 'tip'이다. 정규표현식이 들어간 디버깅을 지원해 주는 IDE 가 있는지는 잘 모르겠다. 난 워낙에 vi 로 터미널에서 작업하기 때문에...