##요약##
트위터에서 @사용자아이디 하거나 http:// url을 입력하면 자동으로 링크를 걸어준다.
파이선과 정규표현식으로 이 기능을 읽기 쉽게, 직관적으로 구현하는 코드조각을 보인다.
##나는 머리도 나쁘고 단순하니까##
나는 머리도 나쁘고, 단순하니까 직관적으로 구현하도록 해보자.
방법도 역시 단순하다. 정규표현식으로 ID와 URL패턴을 각각 만들고 각각을 변환한다.
끝.
##코드조각##
백문이 불여일견이다. 코드를 보자.
[cc lang="python" tab_size="2" lines="60"]
#coding: utf-8
import re
#변환용 패턴
PATTERN = {
‘id’: r’@(?P
‘url’: r’(?P
}
#변환결과
DECORATION = {
‘id’: r”@“>\g
}
def decorate_content(content):
u”’본문을 입력받아 적절한 html문을 추가해준다.”’
decorated_content = ”
#ID 변환
pattern = re.compile(PATTERN['id'])
decorated_content = pattern.sub(DECORATION['id'], content)
#URL 변환
pattern = re.compile(PATTERN['url'])
decorated_content = pattern.sub(DECORATION['url'], decorated_content)
return decorated_content
[/cc]
끝이다. 더 압축하면 코드는 짧아지지만 이해하기가 복잡해지므로 절충했다.
###간단한 설명###
####패턴####
ID패턴에서 ID는 숫자, 문자,_ 로 이루어져 있다고 가정한다. ([a-zA-Z0-9_] == r’\w’ 이다.)
그리고 맨 마지막에는 공백 하나를 줘서 아이디의 구분지점을 공백으로 설정한다
URL패턴은 대부분의 URL을 커버하는데 더 좋은 패턴이 있으면 알아서 사용하면 되겠다.
####함수####
re모듈의 sub 함수는 말그대로 패턴을 찾아 새로운 스트링으로 대치해준다.
이때, 변환결과에서 \g는 정규표현식의 \1(또는 $1) 등과 같은 그룹을 의미한다. \g는 뒤에 그룹명이 와야한다.
###테스트 케이스###
[cc lang="python" tab_size="2" lines="60"]
#coding: utf-8
import re
class UtilityTest(unittest.TestCase):
def setUp(self):
pass
def test_decorate_content(self):
#빈문자열은 빈문자열
content = ”
decorated_content = decorate_content(content)
expected = ”
self.assertEquals(expected, decorated_content)
#패턴매칭이 안되면 원래문자열
content = ‘@ alksjdlf’
decorated_content = decorate_content(content)
expected = ‘@ alksjdlf’
self.assertEquals(expected, decorated_content)
#ID체크
content = ‘@userid is user-id’
decorated_content = decorate_content(content)
expected = “@userid is user-id”
self.assertEquals(expected, decorated_content)
#다중 ID체크
content = ‘@userid and @userid2 is user-id’
decorated_content = decorate_content(content)
expected = “@userid and @userid2 is user-id”
self.assertEquals(expected, decorated_content)
#URL체크
content = ‘http://www.monolith.pe.kr is the url’
decorated_content = decorate_content(content)
expected = “http://www.monolith.pe.kr is the url”
self.assertEquals(expected, decorated_content)
#긴 URL체크
content = ‘http://www.monolith.pe.kr/?url=this_is_url is the url’
decorated_content = decorate_content(content)
expected = “http://www.monolith.pe.kr/?url=this_is_url is the url”
self.assertEquals(expected, decorated_content)
#다중 URL체크
content = ‘http://www.monolith.pe.kr and http://tr.im both are the url’
decorated_content = decorate_content(content)
expected = “http://www.monolith.pe.kr and http://tr.im both are the url”
self.assertEquals(expected, decorated_content)
def tearDown(self):
pass
if __name__ == “__main__”:
unittest.main()
[/cc]
테스트케이스는 저렇게 스태틱하게 상수를 밀어넣으면 안되긴 하지만
짧은 문장이면 손으로 일일이 치는게 원하는 결과를 정확히 예측하는데
다소 도움이 된다.
##클로징##
욕심같아선 decorator를 이용해서 더 간단히 하고 싶지만 일단 여기서 마감.
처음엔 나도 sub를 몰랐는데 있을법한 느낌에 찾아보니 역시나 있다.
아는 것이 힘이라는 것을 다시금 느껴본다. 거인의 어깨위에 서는 버릇을 들이자.
그리고 요담엔 Django를 기반으로 http://tr.im/ 을 구현해보자. (URL을 짧게 줄여주는 사이트이다.)