인코딩과 디코딩, urllib

4 분 소요

ASCII

아스키 코드는 1byte(8 bit) 즉 8자리 2진법 수자로서 256가지 문자를 구별할 수 있다.

print(ord('H'))
# 72
print(ord('e'))
# 101
print(ord('\n'))
# 10

각각의 문자는 숫자에 mapping되는 형식이다.

문제는 70년대 이후 문자의 양이 늘어나며 256자만으로는 전 세계의 여러 문자를 포함시킬 수 없다는 것. 그래서 나온 게 유니코드다.

UNICODE

유니코드는 몇 십 억개의 문자를 나타낼 수 있는 글자당 4byte의 크기를 가지게 된다. 문제는 이렇게 할 경우 낭비되는 공간이 굉장히 많다는 것. 그래서 유니코드와 함께 유니코드를 압축하는 UTF-8, UTF-16, UTF-32 방식 등이 생겨나게 되었다.

파이썬 2.x에서는 유니코드를 나타내고자 하면 문자열 앞에 u를 붙여야만 했지만. 3.x 부터는 기본적으로 문자열은 유니코드다.

# Python 2.7.10 
x = '이광춘'
print(type(x))
# <type 'str'>
x = u'이광춘'
print(type(x))
# <type 'unicode'> 유니코드가 특별한 것. 
# Python 3.5.1
x = '이광춘'
print(type(x))
# <class 'str'>
x = u'이광춘'
print(type(x))
# <class 'str'> 동일하다. 

여기서 이 이야기가 왜 나오냐면 소켓을 통해 데이타를 주고받는 데이타 방식과 파이선 내부에서 쓰는 방식이 다르기 떄문이다.

파이썬: 유니코드 인터넷: UTF-8(95% 이상)

이기 떄문에, 유니코드 <–> UTF-8간 데이타 타입을 바꾸는 것을 인코딩과 디코딩이라고 한다.

유니코드 –> UTF :인코딩 UTF –> 유니코드 :디코딩

앞전 예제에서 썼던 명령어를 살펴보면, 파이썬 내부에서 cmd에 저장한 string(유니코드)을 UTF로 인코딩했다.

import socket

mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mysock.connect(('data.pr4e.org', 80))
cmd = 'GET http://data.pr4e.org/romeo.txt HTTP/1.0\n\n'.encode()
mysock.send(cmd)

그리고 나서 받아들인 데이타는 우리가 다시 읽을 수 있도록 UTF–>유니코드로 디코딩한 것.

while True:
    data = mysock.recv(512)
    if (len(data) < 1):
        break
    print(data.decode())
mysock.close()

urllib

파이썬에서는 urllib 라이브러리를 활용해서 더욱 간단하게 웹 브라우저를 만들 수 있다. 단 4줄로…

import urllib.request, urllib.parse, urllib.error

#file handler 
#인코딩을 자동으로 해 준다. 
fhand = urllib.request.urlopen('http://data.pr4e.org/romeo.txt')
for line in fhand:
	# 디코딩은 수동으로 해줘야 함. 
	print(line.decode().strip())

헤더는 자동으로 생략된다. 원하면 헤더를 꺼낼 수 있지만.

But soft what light through yonder window breaks
It is the east and Juliet is the sun
Arise fair sun and kill the envious moon
Who is already sick and pale with grief

예전에 만든 예제는 위 문장에서 각 단어들의 개수를 세는 dict를 만든 적이 있다.

fhand = urllib.request.urlopen('http://data.pr4e.org/romeo.txt')

counts = dict()
for line in fhand:
	words = line.decode().split() # 한 줄이 words라는 단어들의 list로 변경됨. 
	# words list의 각 단어들에 대해서
	for word in words:
		# count dict에 value로서 개수를 넣어준다.
		# dict 에 word key가 없는 경우, 즉 처음 나오는 단어일 경우 0으로 설정
		# 그 이외에는 + 1씩.
		# 아래의 식은 매우매우매우매우 공식과도 같으므로 외우면 좋다.
		counts[word] = counts.get(word, 0) + 1 
print(counts)

결과는

{'But': 1, 'soft': 1, 'what': 1, 'light': 1, 'through': 1, 'yonder': 1, 'window': 1, 'breaks': 1, 'It': 1, 'is': 3, 'the': 3, 'east': 1, 'and': 3, 'Juliet': 1, 'sun': 2, 'Arise': 1, 'fair': 1, 'kill': 1, 'envious': 1, 'moon': 1, 'Who': 1, 'already': 1, 'sick': 1, 'pale': 1, 'with': 1, 'grief': 1}

댓글남기기