🐌

Pythonで便利なファイル選択関数を作る

に公開

目的

ファイルパスを定義する手間を省きたい。
データを扱う仕事をしていて、大量のファイルと向き合うことが増えてきました。中間データが山ほど生まれますし、元データもバージョンがどんどん増えていき、名称の長いファイルがフォルダにあふれることになります。そしてファイルが増えるたびに、ハードコードしたファイルパスを書き換えていたのですが、次第に面倒を覚えるようになりました。
そこでファイルを特定のディレクトリに突っ込んでおけば、コードを書き換えなくともコンソール上で簡単に選択できるような仕組みを作ることにしました。

関数

from pathlib import Path

def select_file(dir_path: Path, substrings: list[str], index: int | None = None) -> Path | list[Path]:
    """
    ディレクトリ内のファイルを一覧表示し、選択したファイルのPathを取得する。
    substringsを指定することで部分一致検索ができる。
    indexを指定することで追加入力なしでファイルを選択できる。
    index=0のとき、すべてのファイルのPathをリストで返す。
    """

    if not dir_path.is_dir():
        raise ValueError(f"\n\n>>> ■ ERROR: {dir_path} is not a valid directory.\n")

    # ディレクトリ内のファイル一覧を取得
    files = [file for file in dir_path.iterdir()
             if file.is_file() and all(s in file.name for s in substrings) and not file.name.startswith('~$')]

    if not files:
        raise FileNotFoundError(f"\n\n>>> ■ ERROR: No files containing {substrings} found in {dir_path}.\n")

    if index is not None:
        if index == 0:
            return files
        elif 0 < index <= len(files):
            return files[index - 1]
        else:
            raise ValueError(f"\n\n>>> ■ ERROR: Invalid index number. Must be between 0 and {len(files)}.\n")

    # ユーザーにファイルを選択させる
    while True:
        print("\n|● Available files:")
        for i, file in enumerate(files, start=1):
            print(f"|[{i}]. {file.name}")
        
        choice = input("|\n|Choose a file number: ")
        print("|")

        try:
            choice_num = int(choice)
            if 0 < choice_num <= len(files):
                return files[choice_num - 1]
            else:
                print(f"\n>>> ■ ERROR: Invalid number.")
        except ValueError:
            print("\n>>> ■ ERROR: Enter a number.")

パラメータ

  1. dir_path: Path: 検索対象のディレクトリのパス
  2. substrings: list[str]: ファイル名に含まれるべき部分文字列のリスト
  3. index: int | None = None: (オプション)自動選択用のインデックス

使い方

1: 基本

このselect_file関数は、指定されたディレクトリ内のファイルを検索し、数字で選択できるようにします。

from pathlib import Path

data_dir = Path(".") / "data"
selected_file = select_file(data_dir, ["data"])
print(f"Selected file: {selected_file}")
  1. dir_pathには、選択したいファイルが眠るディレクトリのPathを入れます。
  2. substringsには、ファイル名の一部をlist[str]の形式で入れます。ここで指定された文字列を含むファイルに絞り込まれます。拡張子などを入れてあげるのが良いでしょう。

ここまで記述して実行すると、コンソールにて以下のような表示がされます。

|● Available files:
|[1]. data2023.txt
|[2]. data2024.md
|[3]. data2025.ps1
|[4]. data2026.bat
|
|Choose a file number: 

dataディレクトリに含まれていたファイルの内、"data"という文字列を含むファイルが列挙され、それぞれに数字が割り振られます。Choose a file number: の後に数字を入力することで、選択が完了し、ファイルのパスがprintされます。

2: indexの指定

3番目の引数indexは、数字の選択も面倒くさくなった場合の選択肢です。
繰り返しselect_file関数を使い続けると、どのファイルにどの番号が割り振られるのかが分かってきます。
そうなったときは、indexに番号を指定してあげることで、コンソールでの選択すらスキップできます。
(ただしディレクトリ内のファイルが変更される場合には注意を払いましょう。)

# 2番目のファイルを自動選択
selected_file = select_file(data_dir, ["data"], index=2)
print(f"Automatically selected file: {selected_file}")

3: 全ファイルのパスを取得

使い方1: 基本では、ファイルに割り振られる番号は1から始まっていました。
空いている0をindexで指定することで、該当したすべてのファイルのパスをリストで取得できます。
※ index指定なしでコンソールで数字を入力する場合では、0を選択することはできません。

# すべての該当ファイルのパスを取得
all_files = select_file(data_dir, ["data"], index=0)
print("All matching files:")
for file in all_files:
    print(file)

Discussion