1. 텍스트 파일
Python에서 파일을 처리하기 위해선 - 다른 언어에서와 마찬가지로 - 파일을 오픈하고, 데이타를 읽거나 쓴 후, 파일을 닫으면 된다. 파일을 오픈하기 위해서는 내장함수 open()을 사용하는데, 첫번째 파라미터로 오픈할 파일명을 지정하면 된다. open() 함수는 다양한 파라미터를 지정할 수 있는데, 흔히 사용되는 파라마터로 파일모드를 결정하는 mode 와 파일인코딩 방식을 지정하는 encoding 등을 들 수 있다.
파일을 오픈한 후, 파일을 읽기 위해서는 read(), readline(), readlines() 등의 메서드를 사용하고, 쓰기 위해서는 write(), writelines() 등의 메서드를 사용한다. 여기서 한가지 주목할 점은 writeline() 메서드가 없다는 점인데, 다음 라인으로 가기 위해서는 write() 메서드에서 "\n" 를 지정해 주어야 한다. Python은 Universal Newline 이라고 하여 플랫폼에 상관없이 라인 Delimeter로 "\n"을 사용한다. 실행 플랫폼이 윈도우즈의 경우 이 Universal Newline은 CRLF (\r\n) 으로 변경되고, 맥이나 리눅스에서는 LF (\n)로 자동으로 사용된다.
아래 예제는 텍스트 파일에 간단히 두 라인의 텍스트를 쓰는 코드이다.
f = open('test.txt', mode='wt', encoding='utf-8') f.write("Hello, World\n"); f.write("안녕하세요?") f.close()
파일모드
파일모드는 파일을 읽을 것인지 쓸 것인지 혹윽 텍스트 파일인지 바이너리 파일인지 등을 지정할 때 사용한다. 파일모드에는 읽기(r), 쓰기(w 혹은 x), 추가(a), 수정(+) 모드를 지정할 수 있으며, 텍스트 파일(t), 바이너리 파일(b) 를 지정할 수 있다. 특히, w 모드는 파일이 이미 있으면, 먼저 그 내용을 삭제하고 새로 시작하지만, x 모드는 파일이 이미 있으면, FileExistsError 를 발생시킨다. 만약 파일모드를 지정하지 않으면, 디폴트로 텍스트 읽기 (rt) 모드가 설정된다.
파일인코딩
Python의 문자열을 파일로 쓸 때 문자열은 바이트로 변경되는데, 이때 인코딩 방식에 따라 다른 바이트 결과를
가질 수 있다. 또한 파일을 읽어 들일 때 인코딩 방식에 따라 다른 문자열로 읽혀 들일 수 있다.
따라서, 파일을 오픈할 때 encoding 파라미터를 지정해서 어떤 인코딩 방식을 사용하는지 명시적으로 지정는 것이 좋다.
위의 예제에서는 흔히 많이 사용되는 UTF-8 인코딩 방식을 설정하였다.
만약 encoding 파라미터를 지정하지 않으면 시스템 디폴트 인코딩 방식을 사용한다.
시스템 디폴트 인코딩 방식은 아래와 같이 확인할 수 있다.
또 다른 예제로서 아래는 텍스트 파일을 읽는 예이다. 첫번째 [예제 A]는 readline() 메서드를 사용하여 각 라인을 읽어 들여 프린트한 예이다. 이때 첫라인의 데이타를 갖는 s1 은 맨끝에 \n 값을 가지고 있음에 유의하자. 즉, readline() 메서드는 CR/LF 라인 문자를 모두 리턴한다. 따라서 [예제 A]에서 print() 출력을 하게 되면, Hello World 다음에 한 줄이 더 생기게 된다 (print가 마지막에 새 라인으로 이동하므로). 이를 방지하기 위해 [예제 B] 처럼 sys.stdout.write() 를 사용할 수 있다.
[예제 B]는 파일객체와 함께 for 루프를 사용한 예제로서, open() 함수부터 리턴된 파일 Object를 사용하여 for 루프를 돌리면, 파일로부터 한 라인씩 리턴하게 된다.
# 예제 A f = open('test.txt', mode='rt', encoding='utf-8') s1 = f.readline() # Hello, World\n s2 = f.readline() # 안녕하세요? print(s1) print(s2) f.close() # 예제 B import sys f = open('test.txt', mode='rt', encoding='utf-8') for line in f: sys.stdout.write(line) f.close()
2. try...finally 와 with 문
파일을 오픈한 후에는 사용 후 반드시 닫아주어야 하는데, 중간에 에러가 발생하더라도 반드시 닫아주기 위해 아래 예제와 같이 try...finally 를 사용할 수 있다.
try: f = open('test.txt', mode='rt', encoding='utf-8') for line in f: print(line) finally: f.close()
try...finally 를 보다 간결하게 표현하기 위해 아래와 같이 with 문을 사용할 수 있다. 아래 예제에서 with 블력을 벗어나면 파일객체 f 는 자동으로 close된다.
with open('test.txt', mode='rt', encoding='utf-8') as f: for line in f: print(line)
3. 바이너리 파일
바이너리 파일을 읽기 위해서는 파일모드를 rb 로, 쓰기 위해서는 wb 로 지정한다. 파일을 처리를 위해 흔히 사용되는 메서드들로서, read(n) 매소드는 n 바이트만큼의 데이타를 읽어 들이고, ftell() 은 현재의 파일 위치를 리턴하고, fseek()는 특정 위치로 파일 포인터를 옮기는 일을 한다. (주: read(n) 메서드가 텍스트 파일에 사용되면 n 개의 (바이트가 아닌) 문자를 읽어 들인다. 만약 n 을 생략하면 모든 데이타를 읽어 들인다.)
바이너리 파일은 그 자체가 바이트를 처리하는 것이므로 텍스트 파일에서 사용하는 Universal Newline 변환이나 인코딩을 사용하지 않는다.
아래 예제는 이미지 바이너리 파일을 읽어 한 바이트씩 프린트하는 예이다.
with open("test.png", "rb") as f: byte = f.read(1) while byte != b"": print(byte) byte = f.read(1)
또다른 예제로서 아래는 정수 리스트를 바이너리 파일에 출력한 후, 다시 읽어 들여 프린트 해보는 예제이다. 바이너리 파일로 부터 데이타를 읽고 쓸 때는 기본적으로 bytes 클래스를 사용하게 된다. 아래 예제를 보면, 리스트를 파일에 쓰기 전에 bytes()를 호출하여 bytes 타입으로 변경했음을 볼 수 있다. 또한 두번째 바이너리 읽기 예제에서 보듯이 read() 메서드를 써서 리턴되는 객체(content)의 타입은 bytes 클래스이다.
# 바이너리 쓰기 data = [1, 2, 3, 4, 5] with open("test.bin", "wb") as f: f.write(bytes(data)) # 바이너리 읽기 with open("test.bin", "rb") as f: content = f.read() # 모두 읽음 print(type(content)) # bytes class for b in content: print(b)