HOWTO · Python

Python에서 디렉터리의 모든 파일 열기

os.listdir()로 한 디렉터리의 파일을 읽고, glob.glob()으로 일치하는 파일을 선택하고, os.scandir()로 항목을 스캔하거나, os.walk()로 중첩 디렉터리까지 포함합니다.

이 페이지의 내용

디렉터리의 텍스트 파일을 읽으려면 단일 디렉터리에는 os.listdir(), 패턴으로 이름을 선택하려면 glob.glob(), 항목을 검사하려면 os.scandir(), 중첩 디렉터리까지 포함하려면 os.walk()를 사용합니다. 반환된 이름으로 경로를 구성하고 with 문에서 open()을 사용합니다. 이 예제들은 텍스트를 읽습니다. 바이너리 파일에는 "rb"와 같은 바이너리 모드나 형식별 파서가 필요합니다.

os.walk()로 디렉터리의 파일 열기

파일이 시작 디렉터리나 그 아래의 어떤 디렉터리에 있을 수 있을 때는 os.walk()를 사용합니다. 각 반복에서는 현재 디렉터리 경로(root), 그 자식 디렉터리들의 이름(dirnames), 그 디렉터리에 직접 있는 파일들의 이름(filenames)이라는 세 가지 값을 생성합니다. root와 각 파일 이름을 결합하여 경로를 얻습니다. 시작 디렉터리와 파일 이름만 결합하면 중첩 디렉터리의 파일을 놓치게 됩니다.

이 예제는 작성된 그대로 실행할 수 있도록 작은 디렉터리 트리를 만듭니다. 디렉터리와 파일 이름을 정렬하면 이 픽스처의 처리 순서를 예측할 수 있습니다. 그렇지 않으면 디렉터리 순회 순서는 보장되지 않습니다.

이 예제를 실행합니다.

import os
import tempfile

with tempfile.TemporaryDirectory() as directory:
    nested = os.path.join(directory, "notes")
    os.mkdir(nested)
    for folder, name, contents in [
        (directory, "summary.txt", "Ready"),
        (nested, "details.txt", "More notes"),
    ]:
        with open(os.path.join(folder, name), "w", encoding="utf-8") as file:
            file.write(contents)

    for root, dirnames, filenames in os.walk(directory):
        dirnames.sort()
        for filename in sorted(filenames):
            path = os.path.join(root, filename)
            with open(path, encoding="utf-8") as file:
                print(f"{filename}: {file.read()}")

os.walk()는 위에서 아래로, 또는 아래에서 위로 순회할 수 있습니다. 기본값은 위에서 아래로이며, 파일이 발견되는 대로 처리할 때 적합합니다. 이 예제는 명시적으로 UTF-8을 사용합니다. 텍스트 파일에 맞는 인코딩을 선택하세요. 파일마다 인코딩이 다르다면, 하나의 인코딩이 모두에게 적용될 것이라고 가정하지 말고 그 형식에 따라 해당 파일을 처리하세요. 프로세스가 읽을 수 없는 디렉터리에 대해서는 결과적인 파일 시스템 오류를 애플리케이션의 필요에 따라 처리하세요.

os.listdir()로 디렉터리의 파일 열기

os.listdir()는 하나의 디렉터리 바로 안에 있는 항목들의 이름을 반환합니다. 자식 디렉터리로 내려가지 않으며, 그 결과에는 파일과 디렉터리가 모두 포함될 수 있습니다. 아래와 같이 각 이름을 디렉터리 경로와 결합하고, 열기 전에 os.path.isfile()을 확인하세요.

import os
import tempfile

with tempfile.TemporaryDirectory() as directory:
    with open(os.path.join(directory, "one.txt"), "w", encoding="utf-8") as file:
        file.write("First file")
    os.mkdir(os.path.join(directory, "archive"))

    for name in sorted(os.listdir(directory)):
        path = os.path.join(directory, name)
        if os.path.isfile(path):
            with open(path, encoding="utf-8") as file:
                print(f"{name}: {file.read()}")

이 접근 방식은 하나의 디렉터리에 있는 모든 일반 파일이 필요하고 패턴 매칭이 필요 없을 때 유용합니다. os.path.isfile() 검사는 파일에 대한 심볼릭 링크를 따라갑니다. 애플리케이션이 링크를 구분해야 하거나 다른 정책을 적용해야 한다면, 적절한 경로 또는 디렉터리 항목 검사를 사용하세요. 이후 처리가 그 블록을 일찍 빠져나가는 경우를 포함하여, 읽은 뒤에 Python이 파일을 닫도록 각 파일 주위에 with 블록을 유지하세요.

glob.glob()으로 일치하는 파일 열기

glob.glob()은 파일 이름 패턴을 사용하여 경로를 선택합니다. *.txt와 같은 패턴은 지정된 디렉터리에 직접 있는 텍스트 파일과 일치합니다. 그 자체로는 자식 디렉터리를 검색하지 않습니다. 반환된 경로는 open()에 직접 전달할 수 있습니다. 작업이 전체 트리를 방문하는 것이라면 os.walk()를 사용하고, 그게 필요하다면 의도적으로 재귀 패턴을 구성하세요.

import glob
import os
import tempfile

with tempfile.TemporaryDirectory() as directory:
    for name, contents in [("readme.txt", "Instructions"), ("data.csv", "id")]:
        with open(os.path.join(directory, name), "w", encoding="utf-8") as file:
            file.write(contents)

    pattern = os.path.join(directory, "*.txt")
    for path in sorted(glob.glob(pattern)):
        with open(path, encoding="utf-8") as file:
            print(f"{os.path.basename(path)}: {file.read()}")

패턴의 마지막 구성 요소를 변경하여 report-*.csv와 같은 다른 파일 이름 집합을 선택합니다. 이후 작업이 안정적인 순서에 의존할 때 일치 항목을 정렬하면 유용합니다. 패턴은 디렉터리 이름과도 일치할 수 있으므로, 패턴이 파일로 제한되지 않은 경우 os.path.isfile()로 일치하는 경로를 필터링하세요.

os.scandir()로 디렉터리의 파일 열기

os.scandir()는 각 항목의 이름과 경로를 노출하고 is_file()과 같은 메서드를 제공하는 디렉터리 항목 객체를 생성합니다. 루프가 항목을 열기 전에 확인하거나 항목 정보를 검사해야 할 때 유용합니다. 반복자는 컨텍스트 관리자이므로, 반복이 끝나면 with를 사용해 닫으세요. 이 예제는 항목을 확인하고 일반 파일을 읽습니다. 자식 디렉터리로는 재귀하지 않습니다.

import os
import tempfile

with tempfile.TemporaryDirectory() as directory:
    with open(os.path.join(directory, "notes.txt"), "w", encoding="utf-8") as file:
        file.write("Scanned text")
    os.mkdir(os.path.join(directory, "archive"))

    with os.scandir(directory) as entries:
        for entry in sorted(entries, key=lambda item: item.name):
            if entry.is_file():
                with open(entry.path, encoding="utf-8") as file:
                    print(f"{entry.name}: {file.read()}")

entry.name은 스캔한 디렉터리 안에서의 이름이고, entry.path는 그 항목을 열 때 사용할 경로입니다. os.listdir()와 마찬가지로, 단일 os.scandir() 호출은 하나의 디렉터리만 다룹니다. 확인과 열기 사이에 항목이 바뀔 수 있다면, 그 간격 동안 파일 시스템 상태가 변할 수 있으므로, 신뢰할 수 없거나 동시에 수정되는 디렉터리를 처리하는 코드는 열기 시점의 오류도 고려해야 합니다.

디렉터리에 맞는 방법 선택하기

하나의 디렉터리에 대한 간단하고 비재귀적인 패스에는 os.listdir()를 선택하세요. 파일 이름 패턴이 처리할 경로를 결정해야 할 때는 glob.glob()을 선택하세요. 중첩 디렉터리의 파일이 작업의 일부일 때는 os.walk()를 선택하세요. 각 항목에 대해 행동하기 전에 파일 검사나 기타 항목 정보가 필요할 때는 os.scandir()를 선택하세요.

네 가지 방법 모두 디렉터리 내용을 열거하지만, 열거 순서는 보장되지 않습니다. 반복 가능한 처리가 중요할 때는 이름이나 경로를 정렬하세요. 예제들은 컨텍스트 관리되는 파일 객체와 UTF-8 텍스트 모드를 사용합니다. 인코딩을 소스 파일에 맞추고, 파일이 일반 텍스트가 아닐 때는 바이너리 모드나 파서를 사용하세요. 접근 권한, 처리 중 사라지는 파일, 애플리케이션별 인코딩 차이는 프로덕션 코드에서 추가 처리를 요구할 수 있습니다.