[Python101] 004. 파일. 파일을 읽고, 파일에 쓰기

사용자로부터 입력을 받아 데이터를 처리하는 프로그램은 실질적으로 효율에 한계가 있다. 처리해야 하는 데이터를 사용자가 일일이 매번 입력해야 하기 때문이다. 컴퓨터는 귀찮고 반복적으로 처리하는 작업을 수월하게 하라고 있는 것이므로 프로그램은 가능한한 많은 과정을 자동화하는 방향으로 처리하는 것이 좋은 경우가 많다.

보통의 경우에는 다음과 같은 방식의 순서로  작업을 많이 처리하게 된다.

  1. 처리해야 할 데이터를 미리 준비 (텍스트 파일이나 엑셀 파일 등)
  2. 프로그램을 실행할 때 매개변수로 처리할 파일을 주고 실행한다.
  3. 프로그램이 데이터 파일을 읽어 들여서 데이터를 주르르르륵 처리한다.
  4. 처리한 결과는 또 다른 파일에 기록되거나 화면에 출력된다.

이것은 일종의 자동화 작업이고, 파이썬은 이런 작업에 대한 처리를 쉽고 빠르게 처리하기에 매우 좋다.

파일 읽기

데이터를 저장한 파일로부터 데이터를 읽는 방법에 대해 먼저 살펴보도록 하자. 예를 들어 메모장으로 작성한 텍스트 파일의 내용을 읽어들이고, 뭔가 내용을 쓰는 작업은 다음과 같은 절차를 거친다.

  1. 파일을 연다. 파일을 열게 되면 파이썬은 이 파일을 다루기 위한 핸들러(Handler)를 사용하게 된다.
  2. 파일의 내용을 읽어들인다. 파일의 내용을 읽어들이는 작업은 파일을 열면서 생성한 핸들러가 중계해준다. 핸들러는 파이썬 스크립트와 실제 데이터 파일 사이를 중계해주는 모듈이라 생각하면 된다.
  3. 파일에는 새로운 내용을 쓰거나, 기존 파일의 내용 뒤에 내용을 추가하는 것도 가능하다. 이 역시 핸들러에 의해 데이터를 쓸 수 있다.
  4. 파일 다 사용했으면 파일을 닫는다. 만약 파일을 적절히 닫지 않으면 파이썬 프로세스가 정상적으로 종료되지 못하거나, 다른 프로그램이 해당 파일을 쓰지 못하는 경우가 발생할 수 있다.

파일을 열기 위해서는 open 명령을 사용한다. open 명령은 다음과 같이 사용한다.

 파일핸들러 = open('파일의 경로', [모드]) 

open  명령을 사용해서 파일을 열면 파일의 핸들러를 얻을 수 있다. 이 핸들러를 통해 파일의 내용을 읽어들이거나, 파일에 데이터를 쓰는 작업이 가능해진다.

ex09.py

아홉번째 예제는 텍스트 파일을 열어서 그 내용을 보는 예제이다.이 예제를 제대로 실행하기 위해서는 텍스트 파일 하나가 필요하다. 메모장을 열어 아무 텍스트나 몇 줄 입력한 후에 예제를 저장하는 폴더에 저장한다. 이름은 구분하기 쉽게 ex09.txt로 하겠다.

April

Still an irritating wind;
Vestiges of stubborn grey –
Jibing us of recent winter blight.

It’s coming though – like perky breasts
Pushing through a blouse –
Teasing, pleasing in it’s tantalising play:

Warmth of youth in April sun –
Simmering off depression,
Brains retuned; remapped for fun.

April is a portal –
Smoothly transitions
Delicate dispositions – suchlike mine,

Easing hunched bodies into
Summery smiles.

이제 ex09.py 의 소스 코드는 다음과 같다.

from sys import argv

script, filename = argv

txt = open(filename)

print "Here's your file %r:" % filename
print txt.read()

print "Type the filename again:"
file_again = raw_input('> ')

txt_again = open(file_again)
print txt_again.read()

argv 가 등장하는 것을 보니, 파라미터를 받고 실행하는 프로그램이다. 실행은 앞의 강좌에서 소개했던 것과 같은 방식으로 하면 된다.

> python ex09.py ex09.txt

Here's your file 'ex09.txt':
April

Still an irritating wind;
Vestiges of stubborn grey
Jibing us of recent winter blight.

It’s coming though  like perky breasts
Pushing through a blouse
Teasing, pleasing in it’s tantalising play:

Warmth of youth in April sun
Simmering off depression,
Brains retuned; remapped for fun.

April is a portal
Smoothly transitions
Delicate dispositions ? suchlike mine,

Easing hunched bodies into
Summery smiles.
Type the filename again:
> ex09.py
from sys import argv

script, filename = argv

txt = open(filename)

print "Here's your file %r:" % filename
print txt.read()

print "Type the filename again:"
file_again = raw_input('> ')

txt_again = open(file_again)
print txt_again.read()

자 이제 코드를 좀 살펴보자.

1) 3행을 보면 파일 이름을 argv로부터 읽어온다.

2) 5행에서는 이 파일 이름을 사용하여 해당 파일을 연다. 파일 이름만 전달 받았으므로 파이썬 코드 파일과 같은 폴더 내의 파일을 탐색하게 된다. 그리고 파일을 열어서 그 핸들러는 txt라는 이름의 변수에 대입된다.

3) txt는 곧 파일 핸들러라고 했다. 파일 핸들러는 여러 기능 모듈을 가지고 있는데 그 중 하나가 read() 이다. 이 모듈은 파일의 내용을 전부 읽어들이는 일을 한다.

이 때 txt 모듈의 read() 를 실행하기 위해서는 구두점(.)으로 이를 연결해준다. 즉 txt.read() 는 txt 모듈의 read라는 기능 모듈을 실행한다는 뜻이다. 실행이 가능한 모듈은 주로 ()가 뒤에 붙는데, 이렇게 실행이되는 모듈을 특별히 ‘함수’라고 한다.

txt.read() 모듈은 파일 전체의 내용을 읽어 이를 돌려준다. 8행은 이 결과를 모두 출력하게 된다.

4) 10행에서는 다른 파일 이름을 다시 물어본다. 나는 ex09.py를 입력했다. 파이썬 스크립트 소스 자체도 텍스트 파일이다. 단지 관습적으로 .py 라는 확장자를 썼을 뿐이다.

5) 주목할 것은 마지막의 14행이다. txt_again은 파일을 열고 그 핸들러를 대입하게 된다. 즉 txt_again이 파일의 핸들러이고, open() 이 실행된 결과와 동일하다. 즉 open()의 결과가 파일 핸들러라는 이야기다. 따라서 13, 14행은 다음의 한 행으로 합칠 수 있다.

print open(file_again).read() 

파일에 쓰기

ex09.py는 파이썬에서 다른 텍스트 파일의 내용을 읽어와서 사용할 수 있음을 보여주었다. 주소록 정리라든지, 성적 계산, 혹은 이름-이메일 정보를 가진 주소록을 가져와서 받는 사람 주소에 맞게 사람 이름이 들어가는 이메일을 보내는 프로그램 등을 만들 수 있을 것이다.

이번에는 내용을 파일에 기록하는 작업에 대해 살펴보자. 지금까지는 open() 명령에서 모드를 따로 지정하지 않았다. 모드를 따로 지정하지 않은 open()은 기본적으로 “텍스트 파일”을 “읽기”모드로 열게 된다. 파일을 기록하기 위해서는 파일을 w 모드로 열면 된다. w 모드로 파일을 여는 것은 사실 새로운 파일을 만들고 그곳에 내용을 기록하는 것을 의미한다. 따라서 파일 이름이 기존에 있던 내용이면, 그 파일을 덮어쓰게(overwrite) 된다는 점을 참고하자.

ex10.py는 사용자로부터 3줄의 텍스트를 입력받아 이를 파일에 기록해주는 프로그램이다.

from sys import argv

script, filename = argv

print "We're going to erase %r." % filename
print "If you don't want that,hit Ctrl-C(^C)."
print "If you do want that, hit RETURN."

raw_input('?')

print "Opening the file..."
target = open(filename, 'w')

print "Truncating the file. Goodbye!"
target.truncate()

print "Now I'm going to ask you for three lines."

line1 = raw_input("Line 1:")
line2 = raw_input("Line 2:")
line3 = raw_input("Line 3:")

print "I'm going to write these to the file."

target.write(line1)
target.write('\n')
target.write(line2)
target.write('\n')
target.write(line3)
target.write('\n')

target.close()

코드를 보면 알겠지만, 텍스트 파일의 이름을 파라미터로 받고 있다. 따라서 콘솔에서 파일을 실행할 때 텍스트 파일의 이름을 준다. 이 코드에는 파일의 내용을 지우는 truncate() 명령이 포함되어 있는데, 위에서 작성했던 ex09.txt 파일을 변경하도록 해보자. 결과는 아래와 같고, 하이라이트 된 라인은 실제 프롬프트에서 명령을 입력한 라인이다.

c:\Apps\textfiles>c:\Python27\python ex10.py ex09.txt
We're going to erase 'ex09.txt'.
If you don't want that,hit Ctrl-C(^C).
If you do want that, hit RETURN.
?
Opening the file...
Truncating the file. Goodbye!
Now I'm going to ask you for three lines.
Line 1:ABCDEFGHIJKLMNOPQRSTUVWXYZ
Line 2:abcdefghijklmnopqrstuvwxyz
Line 3:0123456789
I'm going to write these to the file.

c:\Apps\textfiles>type ex09.txt
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789

텍스트 파일의 이름을 파라미터로 주고 프로그램을 실행하면 먼저 기존 파일의 삭제 여부를 묻게 된다. 엔터를 누르면 계속 진행을 하게 되고 파일을 지운다. 그런 다음 세 줄의 텍스트를 입력 받고, 이를 파일에 기록하게 된다.type 명령 (터미널에서는 cat 명령)으로 파일의 내용을 살펴보면 입력한 내용으로 해당 파일의 내용이 바뀌어 있음을 볼 수 있다.

코드의 내용을 분석해보도록 하자.

1) 파일의 이름을 filename 이라는 변수에 대입해준다. 이 파일 이름은 파라미터로 전달받은 값이다.

2) 5~7 행에서는 파일의 이름을 표시하고, 취소할 기회를 준다. 이때 ^C (CTRL+C)를 누르면 프로그램의 실행이 취소되고, 엔터를 누르면 다음으로 진행하게 된다.

3) 11~15행에서 해당 파일을 “쓰기 모드”로 열고 내용을 모두 지운다. 파일핸들러의 truncate() 명령은 내용을 모두 지우는 기능을 수행한다. 사실 “w”를 사용해 쓰기 모드로 파일을 열고 다른 내용을 기록하면 이전의 내용은 모두 지워지게 된다. 따라서 15행은 실질적으로는 필요 없는 셈이다. (15행을 주석 처리하고 한 번 실행해보라.)

4) 17~21행에서 다시 각 라인을 입력 받아 line1, line2, line3에 각각 입력 받은 내용을 추가한다.

5) 25~30 행에서는 파일 핸들러 target의 write() 기능을 이용해서 각각의 문자열을 파일에 쓴다. 이 때 각 line1, line2, line3 은 줄바꿈 문자를 포함하고 있지 않으므로 이를 추가해 준다. 물론 이는 target.write(line1+”\n”) 과 같이 써서 줄바꿈 문자를 더한 문자열을 파일에 써도 된다.

6) 끝으로 파일을 닫는다.

물론 하나의 프로그램에서 파일을 2개 이상 동시에 여는 것도 가능하다. 각각의 열린 파일은 핸들러로 조작할 수 있으므로 2개의 핸들러를 open 명령으로 만들어주면 된다.

파일 복사 프로그램

이 글의 서두에서 언급한 파일 복사하는 프로그램을 작성해 보겠다. 사실은 무척이나 단순하다. 이 프로그램은 1) 파라미터로 원본 파일과 복사할 위치를 입력받는다. 2) 원본 파일의 길이(몇 바이트나 되는지)를 알려주고, 3)복사할 위치에 이미 파일이 존재하는지를 알려준다. 그런 다음 4) 원본 파일의 내용을 읽어와서 사본 파일에 쓴다. 5) 두 파일을 닫는다.

ex11.py

이상의 내용을 코딩한 ex11.py의 내용은 다음과 같다.

#-*-coding:utf-8
from sys import argv
from os.path import exists

script, from_file, to_file = argv

print "Copying from %s to %s" % (from_file, to_file)

input = open(from_file)
indata = input.read()
print "The input file is %d bytes long." % len(indata)

print "Does the output file exists? %r" % exists(to_file)
print "Reday, hit RETURN to continue, CTRL-C to abort."
raw_input()

output = open(to_file, 'w')
output.write(indata)

print "Alright, all done."

output.close()
input.close()

실행 결과는 다음과 같다. 이미 만들어져있는 그리고 만만한 ex09.txt를 다른 파일로 복사해보도록 하겠다.

c:\Apps\textfiles>c:\Python27\python.exe ex11.py ex09.txt ex09.bak
Copying from ex09.txt to ex09.bak
The input file is 65 bytes long.
Does the output file exists? False
Reday, hit RETURN to continue, CTRL-C to abort.

Alright, all done.

c:\Apps\textfiles>type ex09.bak
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789

코드 내용 분석을 보자. 무척이나 단순하고 간단한데, 못 보던 기능을 발견했다. 바로 os.path와 exists 라는 명령이다.

1) os.path 로부터 exists라는 모듈을 반입해온다. os 는 파이썬 프로그램이 실행되는 컴퓨터의 운영체제와 관련된 모듈들의 집합이다. 그리고 그 중에는 path라는 모듈이 있는데, 이 모듈은 디스크 내의 경로, 파일 등과 관련된 기능들이 모여있다. exists 모듈은 path에 들어있는 하위 모듈인데, 주어진 경로에 파일이 이미 존재하는지를 검사하여 존재한다면 True를 돌려주는 기능을 한다.

2) input 은 원본 파일을 열어서 그 핸들러를 얻는다. input의 read() 명령을 통해 파일의 전체 내용을 읽어 indata에 대입한다.

3) 13~15는 파일의 길이를 알려주고, 복사본 위치를 찾아 거기에 이미 파일이 있는지를 알려준다. 엔터를 입력하면 raw_input() 뒤의 코드가 계속 실행된다.

4) ‘w’ 모드로 복사본 파일을 연다. 그런 다음, indata를 쓴다.

5) 끝으로 두 파일을 닫으면 된다.

위의 실행 결과에서 알수 있듯이, 파라미터로 준 파일을 보면 내용이 복사되어 있음을 알 수 있다.

사실 파일 입출력은 쉬운 내용은 아니다. ‘핸들러’라는 눈에 보이지도 않는 추상적인 개념을 도구로 다루기 때문이다. 하지만 이런 추상적인 개념은 사실 이해하기보다는 익숙해지면 쉽게 느끼게 된다. (우리가 흔히 하는 덧셈, 뺄셈도 분명 그 원리는 우리가 알고 있지만 엄밀하게는 익숙하게 느끼기 때문에 쉽게 생각할 수 있는 것이다.)

파일 입출력에 대해 간단히 알아보았다. 이로써 기본적인 입력과 출력을 처리할 수 있는 방법에 대해서는 기본기를 파악했으니, 다음 시간부터 실제 프로그램을 움직이는 흐름인 로직에 대해 살펴보기로 하겠다.

[Python101] 002. 변수, 변수를 출력해보다.

파이썬 강좌 세 번째 시간이다. 이번에는 변수란 무엇이고 변수는 출력과 무슨 관계가 있는지를 알아보도록 하자.

초등학교 국어책에는 사진을 보여주고 빈칸에 알맞는 낱말을 넣는 문제들이 종종 나온다.

철수가  ____  봅니다.

이 빈칸에 무엇을 추가해주면 그 때마다 이 문장은 다른 문장이 될 수가 있다.

  1. 철수가 만화책을 봅니다.
  2. 철수가 영화를 봅니다.
  3. 철수가 재미를 봅니다.
  4. 철수가 미쳤나 봅니다.

아무래도 상황에 맞게 가변적인 내용을 출력해주는게, 조금 더 유연하고 쓸모있는 프로그램이 될 수 있는 소지가 클 것이다. 매번 똑같은 말만 나오는 건 좀 재미가 없으니까.

변수

그래서 변수에 대해 알아보도록 하자. 변수는 어떤 값을 담아두는 상자 같은 것이다. 담아두는 값에 따라서 변수는 그 내용이 달라질 수 있다. 따라서 가변적인 처리, 계산등을 할 수 있는 것이다. 변수는 프로그래밍에서 너무나 기본적인 것이라 뭐라 적절하게 설명할 말을 못 찾겠다. 아무튼 우리는 앞으로 변수는 많이, 정말 많이 사용하게 될 것이다.

그런데 조금 주의할 게 있다. 앞서 “변수는 어떤 값을 담아두는 상자”라고 했는데, 이는 사실 나중에 혼란을 줄 수 있는 틀린 표현이다. 개념적으로는 “어떤 값에 대해 이름표를 붙여둔다”라고 생각하는 것이 정확하다.

서점이나 도서관을 예로 들어보자. 도서관에는 많은 책이 있다. 정말 정말 많은 책이 있다. 우리가 도서관에 가서 책을 찾아보고자 할 때 검색을 해 보면 “서지번호”라는 것을 찾게 된다. 이 서지번호에 따라서 책을 찾을 수 있게 된다. 즉 변수는 ‘서지 번호’에 해당한다. 어떤 값에 이름표를 붙여두고, 나중에 다시 찾기 쉽게 표시를 해 두는 것이다. 사실상 ‘그릇에 담는’ 비유와 실질적, 체감적으로는 차이를 인지하기 어려울 수도 있는데, 일단은 그렇게 알아두자. 변수는 값을 담는 그릇이라기보다는 “값을 가리키는” 이정표나 명찰에 더 가까운 개념이다.

변수는 먼저 정의해두고 나중에 그 이름으로 값을 사용하게 된다. 변수를 정의하는 방법은 다음과 같다.

변수이름 = 값

즉 변수 이름에 값을 대입할 때는 등호를 사용한다. 이는 두 개가 같다는 의미가 아니라 변수에 값을 대입한다는 의미이다. (나중에 알게 되겠지만 두 개가 같다는 의미는 == 로 표현한다.) 자 그럼 이제 두 번째 예제인 ex02.py를 작성해 보도록 하자. 요상하게 생긴 표현들이 좀 나온다만 당황하지 말고 우선 “정확하게 타이핑”하고 보도록 하자.

ex02.py

#-*-coding:utf-8

number_of_apple = 50

print number_of_apple
print "Number of apples: ", number_of_apple

people = 5
print "Number of people: ", people
print number_of_apple / people

apple_per_person = number_of_apple / people
print "We can have ", apple_per_person, "apples for everyone."

print
print

# use formatting string
print "There are %d apples in the box" % number_of_apple
print "And here are %d people." % people
print "So, we can have %d apples for everyone." % apple_per_person

print
print

# use formatting string together
print "There are %d apples and we are %d person. \nSo, we can have %d apples for everyone." % (number_of_apple, people, number_of_apple / people)

실행해보면 다음과 같은 결과를 볼 수 있다.

50
Number of apples:  50
Number of people:  5
10
We can have  10 apples for everyone.

There are 50 apples in the box
And here are 5 people.
So, we can have 10 apples for everyone.

There are 50 apples and we are 5 person.
So, we can have 10 apples for everyone.

결과를 보고 나서 다시 한 번 작성한 코드를 이해할 수 있는지 찬찬히 읽어보자. 코드를 읽어보고 무슨 의미일지 생각해본 다음 아래 설명을 보도록 한다.

1) number_of_apple = 50 : 이 문장은 number_of_apple 이라는 변수에 50이라는 값을 대입한다. 즉 숫자값 50에 number_of_apple 이라고 이름표를 붙여 주었다.

변수의 이름은 영문자와 언더스코어(흔히 언더바라고 부르는 _ )그리고 숫자로 구성될 수 있다. 변수 이름에 숫자를 쓰는 것은 그리 좋은 습관이 아니며, 가능하면 간략하고 실제로 어떤 문맥적인 의미를 갖는 한개 혹은 두세개의 단어를 사용하는 것이 좋다. 귀찮다는 이유로 a = 50, u = 70 이런 식으로 쓰는 사람이 많은데, 나중에 이 변수가 무슨 의미인지를 도통 알 수 없게 되는 경우가 많기 때문에 가능하면 단어를 그대로 쓰는 것을 추천한다. 또한 여러 단어를 하나로 합성할 때에는 공백 대신 언더스코어를 써서 변수 이름 자체는 한 단어가 되도록 해야 한다. 혹은 이런 방법 대신에 numberOfApple 과 같은 식으로 두 번째 단어부터는 각 단어의 첫글자를 대문자로 쓰는 표기법도 있다. (아이폰 앱을 만드는 Objective-C 와 같은 언어는 이런 표기법을 추천하고 있다.) 또한 변수명은 숫자로 시작해서는 안된다. 대부분의 프로그래밍 언어가 숫자로 시작하는 이름을 허용하지 않고 있다.

2) 5행은 따옴표 없이 변수 이름을 사용하여 print 하고 있다. 이는 변수의 이름이 아닌 변수의 내용물, 즉 변수가 가리키는 값을 출력해주도록 한다. 사실 print 문은 “계산식”의 결과를 출력하는 개념이라고 이해하면 된다. 변수의 이름만 쓴 계산식은 결국 변수의 값을 말하는 것이니까. print 문은 계산식의 결과를 표시하므로 당연히 다음의 구문을 쓸 수 있다.

print number_of_apple * 10

3) 2)번을 이해했다면 6번 행은 보다 명쾌하게 이해가 될 것이다. 즉 문자열을 출력하고 컴마로 연결했는데, 이이서 변수의 값을 출력해주고 있다.

4) 8~10 행은 다른 변수, people 이라는 변수를 정의하고 출력한다. 그런 다음, 사과의 수를 사람 수로 나눈 수식값을 출력한다. 13행은 문자열과 계산식(변수값), 다시 문자열을 컴마로 연결하여 출력해주는 예를 보여준다.

5) 19~21행은 되게 이상해보이는 구성을 가지고 있다. 먼저 문자열의 내용을 살펴보면 문자열 안에 숫자가 들어가야 할 부분에 %d 라고 써 두었다. 그런 다음 문자열의 뒤에 % 를 붙이고 변수 명을 넣었다.

우리는 이런 것을 ‘문자열 포맷팅’이라고 부른다. 문자열에 특정한 서식을 적용하고, 그 서식에 맞는 값을 치환하여 표시하도록 하는 것이다. 즉 %d 라는 문자는 numbers_of_apple의 숫자값이 치환되어 표시된다. 나머지 두 행도 동일한 방식으로 표시하고 있다.

문자열 포맷팅에서 %d는 digit의 의미로, 숫자를 의미한다. 이와 비슷한 포맷 문자에는 몇 가지가 있는데 가장 흔하게 쓰이는 것들을 소개하겠다.

  • %d : 숫자
  • %f : 소수점이 있는 숫자.
  • %c : 한 글자
  • %s : 문자열
  • %r : 뭐든 상관없음. 변수를 print 한 결과로 치환됨.

6) 이러한 포맷팅이 유요한 것은 마지막 27행에서 드러난다. 27행은 하나의 문자열 안에 이런 치환자를 3개 쓰고 있다. 그리고 문자열 뒤에는 % 를 쓰고 다시 괄호속에 순서대로 들어갈 값을 써주고 있다. 이 때 치환자의 개수와 괄호안의 수식의 개수는 같아야 한다.

다시 한 번 말하지만 포맷팅은 처음에는 이상하게 보여도, 출력하는 용도등으로 쓸 텍스트를 ‘합성’해내는 좋은 방법이다. 익숙해지면 그리 이상하게 보이지도 않고, 또한 어렵지도 않다.

문자열 포매팅

방금 ex02.py를 통해 문자열을 포매팅하는 법을 잠깐 살펴보았다. 포매팅은 문자를 출력하는 것 외에도 문자열을 합성하는데도 유용하게 쓸 수 있다고 했다.

포맷팅 문자열을 만드는 방법은 다음과 같다.

“%d….%f… %r…. %s…” % 치환될 값1, 치환될 값2 ,….)

문자열에 치환자를 포함시키고 문자열 뒤에 %를 표시해서 치환될 값의 묶음과 연결해주면 된다. 그리고 치환될 값이 숫자인지, 소수점을 포함하는 숫자인지, 문자열인지 등에 따라 적절한 치환자를 쓸 수 있으면 되는 것이다.

그럼 이번에는 몇 가지 포매팅을 활용하는 기법을 좀 더 보도록 하자.

ex03.py

ex03.py는 포맷팅을 사용하여 문자열을 합성하고 출력하는 방법을 보여준다. 특이할만한 점은 ‘포맷팅 문자열’ 자체를 변수에 담아두고 출력할 때 합성하는 방법을 쓸 수도 있다는 점이다.

#-*-coding:utf-8

my_name = "sooop"
height = 180
weight = 100
eye = 1.5

print "My name is %s." % my_name
print "I'm %dcm tall and weight %d kg." % (height, weight)
print "My Eyes are %f. I don't need glasses." % eye

print

# string with format
greeting = "Hi, my name is %s" % my_name
print greeting
print

# use format string
more_greet = "Hi, my name is %s. \nI'm %dcm tall and weight %dkg."
print more_greet % (my_name, height, weight)

위의 ex02.py와 마찬가지로 코드의 각 부분이 어떤 의미인지 유추해보고 어떻게 표시될 지 추측해보자. 그리고 실행해보면 다음과 같은 결과가 보이는데, 예상대로 인지 확인해보자.

My name is sooop.
I'm 180cm tall and weight 100 kg.
My Eyes are 1.500000. I don't nee

Hi, my name is sooop

Hi, my name is sooop.
I'm 180cm tall and weight 100kg.

1) 3행에서 이번에는 my_name 이라는 변수에 문자열을 대입했다. 그리고 이를 8행에서 출력한다.

2) 중요한 부분은 15행부터이다. 15행은 print 문이 아니다. greeting 이라는 변수를 정의하면서 이 변수에 포맷 문자열에 다른 변수 값을 합성하여 그 결과를 대입하고 있다. 따라서 greeting은 이 때 “Hi, my name is sooop.”이라는 문자열을 가리키게 된다.

3) 20행에서는 포맷 문자열 그자체를 변수에 대입했다. 21행에서 more_greet 변수 뒤에 치환될 값을 붙여주었다. 따라서 출력되는 시점에 문자열이 합성되고 이를 출력하게 되었다.

ex04.py

한가지 예제를 더 살펴보기로 하자.

# -*-coding:utf-8-*-
print "Mary had a little lamb."
print "Its fleece was white as %s." % 'snow'
print "And everywhere that Mary went."
print "." * 10 # ????

end1 = "C"
end2 = "h"
end3 = "e"
end4 = "e"
end5 = "s"
end6 = "e"
end7 = "B"
end8 = "u"
end9 = "r"
end10 = "g"
end11 = "e"
end12 = "r"

print end1 + end2 + end3 + end4 + end5 + end6,
print end7 + end8 + end9 + end10 + end11 + end12

당연하다고 생각할 수도 있겠지만, 파이썬에서는 “문자열에 대한 연산”을 지원한다. 사실 포매팅 문자열 역시 문자열에 대한 연산인 셈이다. (포맷 문자열만 변수에 저장했다가 print 시에 연산하는 것 처럼 이해할 수 있다.) 3행을 보면 실제로 포매팅 문자열에 치환 값으로 변수가 아닌 문자열 상수를 넣고 있다. 이런 식으로 2개의 문자열을 합성하는 것이 가능하다.

특히 문자열에 대해 +, * 연산이 가능하다. 문자열을 더하면 문자열끼리 연결되고, 숫자를 곱하면 곱한 숫자값만큼 반복된다는 것을 알 수 있다. 표시되는 결과는 다음과 같다.

Mary had a little lamb.
Its fleece was white as snow.
And everywhere that Mary went.
..........
Cheese Burger

포맷팅 문자열을 사용하는 것은 상당히 쓸모가 많은데, 다음 ex05.py를 통해서 한가지만 더 살펴보기로 하자.

ex05.py

formatter = "%r %r %r %r"

print formatter % (1, 2, 3, 4)
print formatter % ("one", "two", "three", "four")
print formatter % (True, False, False, True)
print formatter % (formatter,   formatter,  formatter,  formatter)
print formatter % (
"I had this thing.",
"That you could type up right.",
"But it didn't sing.",
"So I said goodnight."
)

포맷팅 문자열을 변수에 대입한 후 여기에 다양한 치환값을 넣어 매번 다르게 표시하고 있는 것을 확인할 수 있다. 이 예제의 출력은 쉽게 예상할 수 있을 것이다.

1 2 3 4
'one' 'two' 'three' 'four'
True False False True
'%r %r %r %r' '%r %r %r %r' '%r %r %r %r' '%r %r %r %r'
'I had this thing.' 'That you could type up right.' "But it didn't sing." 'So I said goodnight.'

상당히 긴 글이 된 관계로 잠깐 요약을 하고 마무리하겠다. 기본적인 원리와 문법에 대해서는 꼭 기억해야 한다.

  • 문자열 포맷팅은 문자열과 다른 값을 합성하는 방법이다.
  • 문자열 포매팅과 더하기, 곱하기 등은 문자열에 대한 연산이다.
  • 문자열 포매팅은 문자열 % (치환값, 치환값…)의 문법을 사용한다.
  • 변수는 어떤 값을 가리키는 이름표이다.

[Python] SQLite 데이터베이스에 미리 manipulate 하기

영어사전 류의 앱을 만들 때는 미리 데이터를 정제하여 데이터베이스에 밀어 넣어 두는 것이 필요하다. 예를 들어 엑셀 파일이나 텍스트 파일등에 들어있는 데이터가 있다고 한다면 미리 sqlite 파일로 옮겨놓는 작업에 대해 잠깐 메모한다.

먼저 데이터 베이스 파일을 생성한다. 데이터베이스 파일의 생성은 firefox의 sqlmanager를 사용하여 GUI를 통해 생성할 수도 있다. 만약 필드 수가 많다면 일일이 코드를 타이핑하여 스크립트를 만드는 것보다 이를 사용하는 것이 훨씬 쉽다. 그렇지 않다면 다음과 같이 간단히 테이블 생성 쿼리를 보내 DB파일을 생성하는 스크립트를 실행해 준다.

#preparing DB
import sqlite3

filename = 'dictionary.sqlite'
db = sqlite3.connect(filename)
query = '''CREATE TABLE IF NOT EXISTS KRDIC (
        ID INT PRIMARY KEY NOT NULL AUTOINCREMENT,
        KEYWORD TEXT,
        DESCRIPTION TEXT);'''
cursor = db.cursor()
cursor.excute(query)
db.commit()
db.close()

만약 원본 데이터가 엑셀에 있다면 엑셀을 사용해서 각 셀이 탭으로 구분된 텍스트 파일로 저장한다. SQLite에 넣으려면 기본적으로 UTF-8로 인코딩되어야 하는데, 엑셀이 유니코드를 지원하는지는 잘 모르겠다. 아마 CP949로 인코딩된 한글 텍스트 파일일 가능성이 크다. 따라서 스크립트 내에서 인코딩 문제는 처리해야 한다.

아래 스크립트의 주요 내용은 이러하다.

  1. 텍스트 파일을 열고 한 줄의 내용을 읽어들인다.
  2. 한 줄의 내용을 탭 문자로 분리하여 리스트(배열)로 만든다.
  3. KEY 값으로 주기 위해 number 타입 변수를 하나 생성한다.
  4. 각각의 줄에 대해서 KEY 및 각 필드의 값을 추출하여 리스트로 (혹은 튜플로) 만든다.
  5. 루프를 돌면서 한 줄씩 필드의 내용을 DB에 insert 한다
  6. 루프가 끝나면 DB와 파일을 각각 닫는다.
아래는 예제 코드. 마지막 부분의 {python}if __name__ == “__main__”:{/python}은 파일을 바로 실행했을 때, 메인 함수처럼 동작하도록 하는 부분이다.
def fileInsert(sourceFilename = 'kordict.txt'):
   db = sqlite3.connect('dictionary.sqlite')
   cursor = db.cursor()
   f = open(sourceFilename, 'r', encoding='cp949')
   l = f.readline()
   index = 0
   while(l):
       lineItem =  [index,] + l.split('\t')
       recordList = []
       for i in lineItem:
           if (type(i)==type('a')):
               recordList.append(i.encode('utf-8'))
           else:
               recordList.append(i)
           #결손된 라인을 위해 리스트의 원소 개수를 유지
           recordList.append('')

       cursor.execute('''INSERT INTO KRDICT VALUES (?,?,?)''', tuple(recordList[0:3]))
       index = index + 1
       try:
           l = f.leadline()
       except:
           print("error")
    print("complete")
    db.commit()
    db.close()
    f.close()

if __name__ == "__main__":
    fileInsert() 

20091204 :: py2exe로 단일 실행 파일 만들기

이전에도 한 번 py2exe 모듈을 사용해서 파이썬 스크립트를 실행가능한 exe 파일로 만드는 부분에 대해서, 언급을 한 적이 있지요. 이렇게 py2exe를 사용하여 실행파일을 작성하면 파이썬 인터프리터가 lib 형태로 함께 만들어집니다. 이 것을 하나의 단일 실행 파일로 만드는 방법을 소개하고자 합니다.

py2exe를 사용하여 빌드 스크립트를 작성할 때 option 값 내에 py2exe 부분에 bundlefiles 옵션을 1로 주게되면 모든 파일을 하나로 묶어주게 됩니다. 여기에 zipfile=none 옵션을 주면 이러한 묶어진 파일이 실행 파일 내부로 들어가게 되지요.

이와 별도로 dll 파일들은 실행 파일 내에 포함이 되지 않는 듯 합니다. NSVCR71.dll 파일 같은 경우는 매우 빈번히 dist 디렉토리에 같이 들어가더군요. 아래 예제 코드를 참조하세요~ (*왠지 wp-codebox 플러그인의 다운로드 기능이 정상적으로 작동하지 않는 듯 하네요…)

#-*-coding:utf-8
#setup.py
from distutils.core import setup
import py2exe, sys

sys.argv.append("py2exe")

setup(console=["somefile.py"],\
      options={\
            "py2exe":{\
                  "packages" : ["pywinauto", "pywinauto.controls", "pywinauto.application"], \
                  "bundle_files":1,\
                  "optimize":2,
                  }
            },
      zipfile = None
      )

♦ Cruithne님이 지적해 주신 코드 내 오타 수정하였습니다. 감사드립니다. (11.07.10)

20090404 :: 파이썬에서 환경 파일 다루기

얼마전 서브 블로그를 통해서 포스팅한 ‘netsh’를 사용해서 유선랜 설정 바꾸는 스크립트를 만들어서 잘 활용하고 있습니다. 요즘은 집 – 사이트를 왔다 갔다 하고 있어서 하루에 두 번씩은 랜 연결의 ip 설정을 바꿔주게 되는데요, 두 개의 배치 파일을 만들고 입력값에 따라 각각 배치 파일을 실행해주는 파이썬 스크립트를 사용중입니다. 그런데 참 욕심이라는게, 다른 설정을 필요로 하는 경우에도 손쉽게 이를 추가하면 좋겠다는 생각이 들더군요. 그래서 그 스크립트를 업그레이드 하기로 결심했습니다. 외부 설정 파일을 사용하여 여러 군데의 유선 랜 설정 정보를 저장해 두고, 프로그램 실행 시 연결을 선택하여 설정을 바꿀 수 있는 프로그램으로 말이지요. 실제 작동 자체는 딱 2개 라인이면 되겠지만, 설정 파일을 이용하는 것 자체가 해 본 적이 없어서 그런지 만만하지가 않을 듯 합니다.

1. ini 파일을 사용하기

첫번 째로 ini 파일을 사용하는 방법이 있습니다. 사실 많은 분들이 사용하는 방법 중에 하나 입니다. 단순히 메모장으로 설정 파일 편집이 가능하고 간단하게 주석처리하여 설정을 바꿀 수 있는 장점이 존재하지요. 다만 특정한 한 가지씩의 속성 값만을 외부 파일에 정의할 때 간편하고 빠른 방법임에는 분명합니다만, 기본적으로 텍스트 파일에 정보를 담아두는 경우에는 라인 별로 처리하게 되므로 동일한 유형의 여러 가지 데이터를 미리 담아두기에는 다소 어려움이 많습니다. (구분자를 적절히 이용할 수 있겠습니다만, 그러면 사람이 수동으로 설정을 편집하기가 쉽지 않을 수 있지요)

2. py 파일을 반입하기

설정을 하나의 사전 혹은 사전의 리스트 형태로 만들어서 저장하고 변경하는 방법입니다. 실제로 많은 파이썬 프로그램이 채용하고 있는 방식이기도 합니다. 대표적으로 django의 settings.py 파일이 이러한 예에 속하지요. 설정 로딩 자체를 import를 통해서 해버리면 그만입니다. 별도의 파싱이 필요없다는 최고의 장점을 지니고 있지요. 다만, 설정 파일을 수정하면서 오타를 낸다면 프로그램 자체가 설정 파일을 무시하지 못하고 실행이 안되는 문제가 있기는 합니다. 게다가 py2exe 등을 써서 실행 파일로 배포하는 경우, 아마 반입되는 파일들이 함께 컴파일 되는 것으로 알고 있어서 사용할 수 없는 것으로 알고 있습니다.

3.xml  파일을 사용하기

세번 째로 xml 파일을 사용하는 방법을 생각할 수 있습니다. xml 파일로 설정 파일을 대체하는 경우라면, py2exe 등의 툴로 실행 파일을 배포해도 무관하게 쓸 수 있다는 장점이 있고, 설정 편집 기능을 프로그램 자체에서 제공할 수도 있습니다. 게다가 파싱도 minidom과 같은 파서를 사용하면 그만인 부분이 있지요. 물론 형식에 맞지 않는 xml 파일은 오류를 일으킬 수 있지만, 이는 충분히 방어 가능한 수준일 것으로 생각됩니다. 다만, 이러한 파서를 쓰게 되면 실행 파일로 만들어 배포할 때는 용량이 다소 커지지 않을까 생각은 드는 군요. 

대략 이런 세 가지 방법이 생각 납니다. 혹 다른 좋은 아이디어나 방법이 있다면 댓글로 알려주시면 감사하겠습니다. 그리고 혹 저와 같은 필요성을 가지는 분들이 계시다면, 나중에 해당 프로그램을 만들어서 성공하면, 부끄럽지만 살짝 공개해 볼까도 합니다.