2.27. 읽기와 쓰기

파일은 디스크에 존재합니다. Python은 open() 을 통해 파일에 접근하며, 이는 파일 객체 를 반환하고 그 메서드들이 기저의 바이트를 읽고 씁니다.

2.27.1. open과 모드

첫 번째 인수는 경로이고, 두 번째는 모드 입니다 – 파일을 어떻게 사용할지를 Python에 알려주는 짧은 문자열입니다:

  • "r" – 읽기(기본값). 기존 파일을 읽기 위해 엽니다.

  • "w" – 쓰기. 새 파일을 만들거나 기존 파일을 비어 있도록 잘라냅니다(truncate).

  • "a" – 추가. 잘라내지 않고 파일의 끝에서 쓰기 위해 엽니다.

  • 위의 어느 것에든 붙인 "b" ("rb", "wb", "ab") – 바이너리 모드. 파일의 내용이 str 이 아니라 bytes 입니다.

f = open("notes.txt", "r")
text = f.read()
f.close()

2.27.2. 컨텍스트 관리자를 사용하기

위 패턴은 open()close 사이의 무언가가 예외를 발생시키면 파일 핸들을 누수시킵니다. 해결책은 with 문입니다(컨텍스트 관리자 참조):

with open("notes.txt") as f:
    text = f.read()

# f is closed here, even if read() failed

이것이 표준 형태입니다 – 매번 이렇게 작성하십시오.

2.27.3. 읽기

파일 객체는 여러 가지 읽기 방식을 지원합니다:

  • io.IOBase.read() – 파일 전체(또는 N바이트)를 읽어 단일 문자열(또는 바이트 객체)로 반환합니다.

  • io.IOBase.readline() – 끝의 "\n" 을 포함하여 한 줄을 읽습니다.

  • 파일을 직접 순회하면 한 번에 한 줄씩 나오며, 파일 전체를 한꺼번에 읽는 것보다 메모리 사용량이 훨씬 적습니다.

with open("log.txt") as f:
    for line in f:
        print(line.rstrip())

str.rstrip() 은 출력하기 전에 끝의 개행을 제거하여 출력이 두 줄 간격으로 벌어지지 않게 합니다.

2.27.4. 쓰기

파일을 "w" 모드로 열고 io.IOBase.write() 를 사용합니다:

with open("out.txt", "w") as f:
    f.write("hello\n")
    f.write("world\n")

io.IOBase.write() 는 개행을 추가하지 않습니다 – 여러분이 준 바이트(텍스트 모드에서는 문자)를 정확히 그대로 씁니다.

2.27.5. 텍스트 대 바이너리

텍스트 모드(기본값, "b" 없는 "r" / "w")는 들어오는 바이트를 기본 인코딩을 사용해 str 로 디코딩하고, 나가는 str 을 다시 바이트로 인코딩합니다. 설정, 로그, JSON 등 텍스트인 모든 것에 사용하십시오.

바이너리 모드("rb" / "wb")는 디코딩 단계를 건너뛰고 bytes 를 반환합니다. 이미지, struct로 패킹된 레코드, 네트워크 캡처 등 모든 바이트가 중요하고 파일이 사람이 읽을 수 없는 모든 것에 사용하십시오.

2.27.6. 파일 목록 보기와 제거하기

os 모듈은 파일 객체 자체에는 없는 파일 시스템 연산을 노출합니다:

  • os.listdir() – 디렉터리 안의 이름 목록을 반환합니다.

  • os.remove() – 파일을 삭제합니다.

  • os.rename() – 파일 이름을 변경합니다.

  • os.stat() – 파일 메타데이터(크기, 수정 시각, …).

  • os.mkdir() – 새 디렉터리를 만듭니다.

import os

for name in os.listdir("/"):
    print(name)

파일이나 디렉터리가 없을 수 있는 경우 이 연산들 주위에서 OSError 를 잡으십시오 – 이 연산은 실제 스크립트에서 예외가 자주 나타나는 곳 중 하나입니다.