입력과 출력

프로그램이 사용자와 상호작용해야 하는 상황이 있을 것입니다. 예를 들어, 사용자에게 입력을 받아 어떤 결과를 출력하고 싶을 수 있습니다. 이 경우 input 함수와 print 함수를 각각 이용해 처리할 수 있습니다.

출력의 경우 str(문자열) 클래스의 다양한 메서드를 사용할 수도 있습니다. 예를 들어, rjust 메서드를 이용해 지정한 너비로 우측 정렬한 문자열을 구할 수 있습니다. 자세한 사항은 help(str)을 참고하세요.

흔히 볼 수 있는 또 다른 입출력 유형은 파일을 처리하는 것입니다. 파일을 생성하고, 파일을 읽고 쓰는 기능은 수많은 프로그램에 반드시 필요한 기능이므로 이번 장에서는 이러한 측면을 살펴보려고 합니다.

사용자 입력

다음 프로그램을 io_input.py라는 이름으로 저장합니다.

def reverse(text):
    return text[::-1]


def is_palindrome(text):
    return text == reverse(text)


something = input("Enter text: ")
if is_palindrome(something):
    print("Yes, it is a palindrome")
else:
    print("No, it is not a palindrome")

출력 결과:

$ python3 io_input.py
Enter text: sir
No, it is not a palindrome

$ python3 io_input.py
Enter text: madam
Yes, it is a palindrome

$ python3 io_input.py
Enter text: racecar
Yes, it is a palindrome

동작 원리

예제에서는 슬라이싱을 이용해 텍스트를 거꾸로 뒤집습니다. a 위치에서 시작해 b 위치까지 슬라이싱하는 seq[a:b]를 이용해 시퀀스를 슬라이싱하는 방법은 이미 앞에서 살펴봤습니다. 또한 슬라이싱할 때 간격을 나타내는 세 번째 인수를 지정할 수도 있습니다. 기본 간격은 1인데, 이 경우 텍스트의 일부가 연속된 상태로 반환됩니다. 간격을 음수로 지정하면, 즉 -1을 지정하면 거꾸로 뒤집은 텍스트를 반환합니다.

input() 함수는 문자열을 인수로 받아 사용자에게 표시합니다. 그런 다음, 사용자가 뭔가를 입력하고 엔터 키를 누를 때까지 기다립니다. 사용자가 문자열을 입력하고 엔터 키를 누르면 input() 함수가 사용자가 입력한 텍스트를 반환합니다.

예제에서는 텍스트를 받아 그것을 뒤집습니다. 원본 텍스트와 뒤집은 텍스트가 동일하면 그 텍스트는 회문(palindrome)입니다.

과제

텍스트가 회문인지 검사할 때는 구두점, 공백, 대소문자도 무시해야 할 것입니다. 예를 들어, "Rise to vote, sir."은 회문이지만 현재 프로그램에서는 이를 회문으로 판단하지 않습니다. 프로그램을 개선해서 이를 회문으로 인식하도록 만들 수 있을까요?

힌트가 필요하다면 한 가지 알려드리겠습니다...1

파일

file 클래스의 객체를 하나 생성하고 파일로부터 읽거나 파일에 쓰는 read, readline, write 메서드를 이용하는 식으로 파일을 열고 읽거나 쓸 수 있습니다. 파일을 읽거나 파일에 쓸 수 있는지는 파일을 열 때 지정하는 모드에 따라 달라집니다. 그러고 나서 파일 처리가 끝나면 close 메서드를 호출해 파이썬에게 파일 사용이 끝났음을 알립니다.

예제(io_using_file.py):

poem = '''\
Programming is fun
When the work is done
if you wanna make your work also fun:
    use Python!
'''

# 쓰기('w'riting) 모드로 엽니다
f = open('poem.txt', 'w')
# 텍스트를 파일에 씁니다
f.write(poem)
# 파일을 닫습니다
f.close()

# 모드를 지정하지 않으면 기본적으로
# 읽기('r'ead) 모드로 가정합니다
f = open('poem.txt')
while True:
    line = f.readline()
    # 길이가 0이면 EOF을 나타냅니다
    if len(line) == 0:
        break
    # 파일에서 읽어오고 있으므로
    # `line`의 각 줄 끝에는 
    # 개행 문자가 있습니다
    print(line, end='')
# 파일을 닫습니다
f.close()

출력 결과:

$ python3 io_using_file.py
Programming is fun
When the work is done
if you wanna make your work also fun:
    use Python!

동작 원리

open 메서드를 이용하기만 해도 새 파일 객체를 생성할 수 있다는 점에 유의합니다. 파이썬에 내장된 open 함수에다 파일의 이름과 파일을 어떻게 열고 싶은지를 나타내는 모드를 지정해 파일을 엽니다(또는 파일이 존재하지 않을 경우 파일을 생성합니다). 모드로는 읽기 모드('r'), 쓰기 모드('w'), 추가 모드('a')를 지정할 수 있습니다. 또한 텍스트 모드('t')나 바이너리 모드('b')로 파일을 읽거나 쓰거나 추가하는지도 지정할 수 있습니다.

예제에서는 먼저 텍스트 쓰기 모드로 파일을 열고(생성하고) 파일 객체의 write 메서드를 이용해 poem 문자열 변수를 파일에 쓴 다음 마지막으로 close로 파일을 닫습니다.

다음으로 같은 파일을 다시 읽기 모드로 엽니다. 이때 모드를 지정할 필요는 없는데, '텍스트 파일 읽기'가 기본 모드이기 때문입니다. 그러고 나서 반복문에서 readline 메서드를 이용해 파일의 각 줄을 읽습니다. 이 메서드는 각 줄 끝에 위치한 개행 문자까지 포함해서 한 줄 전체를 반환합니다. 문자열이 반환되면 파일의 끝에 도달했다는 것을 의미하므로 반복문에서 빠져나옵니다.

마지막으로 파일을 닫습니다(close).

readline의 출력 결과를 통해 이 프로그램이 실제로 poem.txt 파일에 쓰고 새로 만들어진 poem.txt 파일로부터 읽었다는 것을 확인할 수 있습니다.

피클

파이썬에서는 어떠한 일반 파이썬 객체라도 파일에 저장하고 나서 나중에 이를 되돌려주는 pickle이라는 표준 모듈을 제공합니다. 이를 가리켜 객체를 영구적으로(persistently) 저장한다고 표현합니다.

예제(io_pickle.py):

import pickle

# 객체를 저장할 파일의 이름
shoplistfile = 'shoplist.data'
# 구입할 품목의 리스트
shoplist = ['apple', 'mango', 'carrot']

# 파일에 씁니다
f = open(shoplistfile, 'wb')
# 객체를 파일에 저장합니다
pickle.dump(shoplist, f)
f.close()

# shoplist 변수를 삭제합니다
del shoplist

# 저장장치에서 읽어옵니다
f = open(shoplistfile, 'rb')
# 파일에서 객체를 불러옵니다
storedlist = pickle.load(f)
print(storedlist)
f.close()

출력 결과:

$ python io_pickle.py
['apple', 'mango', 'carrot']

동작 원리

객체를 파일에 저장하려면 먼저 open으로 파일을 바이너리 쓰기 모드(wb)로 연 다음 pickle 모듈의 dump 함수를 호출해야 합니다. 이 과정을 피클링(pickling)이라 합니다.

다음으로 객체를 반환하는, pickle 모듈의 load 함수를 이용해 객체를 조회합니다. 이 과정을 언피클링(unpickling)이라 합니다.

유니코드

지금까지 문자열을 만들고 사용하거나 파일로부터 문자열을 읽거나 파일에 쓸 때는 간단한 영문자만 사용했습니다. 영어 및 비영어 문자 모두 유니코드(Unicode)로 표현할 수 있으며(유니코드에 대해서는 이번 절 끝에 있는 글을 읽어보세요), 파이썬 3에서는 기본적으로 문자열 변수(작은따옴표나 큰따옴표, 삼중따옴표로 작성한 모든 텍스트)를 유니코드로 저장합니다.

참고: 파이썬 2를 사용 중이고 영어가 아닌 언어를 읽거나 쓰고 싶다면 unicode 타입을 이용해야 하며, 이 경우 u 문자로 시작합니다(예: u"hello world").

>>> "hello world"
'hello world'
>>> type("hello world")
<class 'str'>
>>> u"hello world"
'hello world'
>>> type(u"hello world")
<class 'str'>

데이터가 인터넷을 통해 전송될 때는 데이터를 바이트, 즉 컴퓨터가 쉽게 이해할 수 있는 형태로 전송해야 합니다. 유니코드(파이썬이 문자열을 저장할 때 사용하는)를 바이트로 번역하는 규칙을 인코딩(encoding)이라 합니다. 널리 사용되는 인코딩은 UTF-8입니다. open 함수에서 다음과 같은 간단한 키워드 인수를 이용해 UTF-8 형식으로 파일을 읽거나 쓸 수 있습니다.

# encoding=utf-8
import io

f = io.open("abc.txt", "wt", encoding="utf-8")
f.write(u"Imagine non-English language here")
f.close()

text = io.open("abc.txt", encoding="utf-8").read()
print(text)

동작 원리

메시지를 인코딩하는 첫 번째 파일 열기 문장에서 io.openencoding 인수를 사용한 다음, 메시지를 디코딩하는 두 번째 파일 열기 문장에서 다시 한번 사용합니다. 텍스트 모드로 파일을 열 때만 인코딩을 사용해야 한다는 점에 유의합니다.

위와 같이 유니코드 리터럴(문자열 앞에 u를 지정해)을 사용하는 프로그램을 작성할 때마다 프로그램에서 UTF-8을 사용한다고 파이썬에게 알려줘야 하며, 이를 위해 프로그램의 맨 위에 # encoding=utf-8이라는 주석을 추가해야 합니다.

이 주제에 대해서는 다음 글을 통해 좀 더 자세히 배울 수 있습니다.

정리

지금까지 파일 처리와 피클 모듈, 유니코드에 관한 다양한 유형의 입출력을 살펴봤습니다.

다음 장에서는 예외의 개념을 알아보겠습니다.


1. 튜플(모든 문장부호는 이곳에서 확인할 수 있습니다)을 사용해 모든 사용 금지된 문자를 담아둔 다음, 멤버십 테스트를 이용해 특정 문자를 제거해야 할지 여부를 판단하면 됩니다. 즉, forbidden = ('!', '?', '.', ...)과 같이 작성합니다.