Python, pathlibでファイル名・拡張子・親ディレクトリを取得

Modified: | Tags: Python, ファイル処理

Pythonのpathlibモジュールを使ってパスからファイル名(basename)や拡張子、親ディレクトリ(フォルダ)などを取得する方法を説明する。

パスの文字列を使ってファイル名などを取得する、従来のos.pathについては以下の記事を参照。

本記事のサンプルコードでは以下のようにpathlibモジュールからPathクラスをインポートしている。pathlibは標準ライブラリに含まれているので追加のインストールは不要。

from pathlib import Path

Pathオブジェクトの基礎

Path()Pathオブジェクトを生成して操作する。引数に相対パスまたは絶対パスの文字列を指定する。

p_file = Path('dir/sub_dir/file.txt')
print(p_file)
# dir/sub_dir/file.txt

print(type(p_file))
# <class 'pathlib.PosixPath'>

OSによってPosixPathまたはWindowsPathのインスタンスが生成される。

なお、以降では純粋パスPurePathの属性やメソッドも紹介するが、具象パスPathは純粋パスPurePathのサブクラスなので、PathPosixPathまたはWindowsPath)のインスタンスからでも使用可能。

詳しくは以下の記事を参照。

ファイル名(basename)・ディレクトリ名を取得: name, stem

パスの末尾のファイル名(basename)の文字列を取得するにはname属性を使う。

p_file = Path('dir/sub_dir/file.txt')

print(p_file.name)
# file.txt

print(type(p_file.name))
# <class 'str'>

拡張子を除いたファイル名の文字列はstem属性で取得可能。

print(p_file.stem)
# file

print(type(p_file.stem))
# <class 'str'>

パスの末尾の要素がピリオド.を含まない、拡張子なしのファイルやディレクトリの場合、namestemどちらも末尾の要素(ファイル名やディレクトリ名)の文字列を返す。

p_dir = Path('dir/sub_dir/')
print(p_dir)
# dir/sub_dir

print(p_dir.name)
# sub_dir

print(p_dir.stem)
# sub_dir

拡張子を取得: suffix, suffixes

拡張子はsuffix属性で取得できる。ピリオド.付きの文字列となる。

p_file = Path('dir/sub_dir/file.txt')

print(p_file.suffix)
# .txt

print(type(p_file.suffix))
# <class 'str'>

パスの末尾の要素がピリオドを含まない場合は空文字列が返される。

p_dir = Path('dir/sub_dir/')

print(p_dir.suffix)
# 

ピリオドが必要ない場合はlstrip()で先頭のピリオドを削除するか、スライスで2文字目以降を取得する。

print(p_file.suffix.lstrip('.'))
# txt

print(p_file.suffix[1:])
# txt

パスの末尾の要素がピリオドを含まない場合はどちらの方法でも空文字列のまま。

print(p_dir.suffix.lstrip('.'))
# 

print(p_dir.suffix[1:])
# 

.tar.gzのように、ピリオドが複数含まれている場合、suffixは末尾のみを返す。suffixes属性を使うと、拡張子のリストを取得できる。

p_tar_gz = Path('dir/sub_dir/file.tar.gz')
print(p_tar_gz)
# dir/sub_dir/file.tar.gz

print(p_tar_gz.suffix)
# .gz

print(p_tar_gz.suffixes)
# ['.tar', '.gz']

ピリオドが一つまたは無しの場合は、要素数一個のリストまたは空のリストとなる。

print(p_file.suffixes)
# ['.txt']

print(p_dir.suffixes)
# []

親ディレクトリを取得: parent, parents

parent属性は親ディレクトリのPathオブジェクトを返す。os.path.dirname()に相当するが、parentが返すのは文字列ではなくPathオブジェクト。

p_file = Path('dir/sub_dir/file.txt')

print(p_file.parent)
# dir/sub_dir

print(type(p_file.parent))
# <class 'pathlib.PosixPath'>

親ディレクトリの名前のみを文字列で取得したい場合は上述のname属性を使えばよい。

print(p_file.parent.name)
# sub_dir

print(type(p_file.parent.name))
# <class 'str'>

上位階層に一気にアクセスしたい場合はparent.parentのようにparentを繰り返してもいいが、parents[]を使うと便利。parents[0]が一階層上(親ディレクトリ)、parents[1]が二階層上、parents[2]が三階層上…となる。

print(p_file.parents[0])
# dir/sub_dir

print(p_file.parents[1])
# dir

print(p_file.parents[2])
# .

例のように相対パスで指定した場合、カレントディレクトリより上位階層にはいけない。

# print(p_file.parents[3])
# IndexError: 3

さらに上の階層にアクセスしたい場合はresolve()で絶対パスに変換する。

p_abs = p_file.resolve()
print(p_abs)
# /Users/mbp/Documents/my-project/python-snippets/notebook/dir/sub_dir/file.txt

print(p_abs.parents[3])
# /Users/mbp/Documents/my-project/python-snippets

当然ながら、この場合もルートより上の位置を指定するとエラーとなる。

# print(p_abs.parents[9])
# IndexError: 9

Python 3.10からparentsに負のインデックスを指定できるようになった。-1がカレントディレクトリまたはルートに対応する。

print(p_file.parents[-1])
# .

print(p_file.parents[-2])
# dir

print(p_file.parents[-3])
# dir/sub_dir

print(p_abs.parents[-1])
# /

print(p_abs.parents[-2])
# /Users

print(p_abs.parents[-3])
# /Users/mbp

..を含む場合の注意

相対パスで一階層上を表す..を使っている場合は注意が必要。

同じファイルを指す以下の2つのPathオブジェクトを例とする。

p_file = Path('dir/sub_dir/file.txt')
print(p_file)
# dir/sub_dir/file.txt

p_file_rel = Path('dir/sub_dir/../sub_dir/file.txt')
print(p_file_rel)
# dir/sub_dir/../sub_dir/file.txt

この2つのPathオブジェクトのparents[]は以下のようになる。

print(p_file.parents[0])
# dir/sub_dir

print(p_file.parents[1])
# dir

print(p_file.parents[2])
# .

print(p_file_rel.parents[0])
# dir/sub_dir/../sub_dir

print(p_file_rel.parents[1])
# dir/sub_dir/..

print(p_file_rel.parents[2])
# dir/sub_dir

parentおよびparents[]は純粋な字句操作であるため、同じファイルやディレクトリを示していても、..の有無によって結果が異なる。

resolve()で絶対パスに変換すると..は除去されるので、..を含んでいる場合は、resolve()のあとでparentparents[]を使うと間違いを防げる。

print(p_file.resolve())
# /Users/mbp/Documents/my-project/python-snippets/notebook/dir/sub_dir/file.txt

print(p_file_rel.resolve())
# /Users/mbp/Documents/my-project/python-snippets/notebook/dir/sub_dir/file.txt

print(p_file.resolve() == p_file_rel.resolve())
# True

..を除去した上でカレントディレクトリからの相対パスに変換するには、resolve()relative_to(), cwd()を使う。

print(p_file_rel.resolve().relative_to(Path.cwd()))
# dir/sub_dir/file.txt

pathlibにおける絶対パス・相対パスの処理についての詳細は以下の記事を参照。

同じディレクトリの別のファイルを取得: with_name()

with_name()メソッドは、元のパスのname属性を変更したPathオブジェクトを返す。引数に新たなname属性を文字列で指定する。

例えば、ファイルを指すPathオブジェクトから別のファイル名を指定してwith_name()を使うと、同じディレクトリ(同じ階層)の別のファイルを指すPathオブジェクトが取得できる。

p_file = Path('dir/sub_dir/file.txt')

print(p_file.with_name('new_file.txt'))
# dir/sub_dir/new_file.txt

print(type(p_file.with_name('new_file.txt')))
# <class 'pathlib.PosixPath'>

with_name()はあくまでも元のパスのname属性の変更。例えばディレクトリを示すPathオブジェクトからwith_name()で新たなファイル名を指定することもできる。

p_dir = Path('dir/sub_dir/')

print(p_dir.with_name('new_dir'))
# dir/new_dir

print(p_dir.with_name('new_file.txt'))
# dir/new_file.txt

なお、ディレクトリの中に新たなディレクトリやファイルを追加したパスを取得するにはjoinpath()/演算子を使う。

print(p_dir.joinpath('new_dir'))
# dir/sub_dir/new_dir

print(p_dir / 'new_file.txt')
# dir/sub_dir/new_file.txt

with_name()は同じディレクトリに新たなファイルやディレクトリを作成するのに便利。新たなファイル名やディレクトリ名を指定してまだ存在しないパスのPathオブジェクトを取得し、新規ファイル・ディレクトリを作成できる。

新しいディレクトリを作成するmkdir()メソッド、および、空のファイルを作成するtouch()メソッドを使った例は以下の通り。

p_file.with_name('new_dir').mkdir(parents=True)
p_file.with_name('new_file.txt').touch()

pathlibを使ったファイルやディレクトリの作成は以下の記事を参照。

拡張子を変更したパスを取得: with_suffix()

with_name()に似たメソッドにwith_suffix()がある。

with_suffix()は元のパスのsuffix属性(拡張子)を変更したPathオブジェクトを返す。引数に新たな拡張子を文字列で指定する。

p_file = Path('dir/sub_dir/file.txt')

print(p_file.with_suffix('.text'))
# dir/sub_dir/file.text

print(type(p_file.with_suffix('.text')))
# <class 'pathlib.PosixPath'>

先頭のピリオド.がないとエラーになるので注意。

# print(p_file.with_suffix('text'))
# ValueError: Invalid suffix 'text'

拡張子なしのファイルやディレクトリの場合、指定した拡張子が追加される。

p_dir = Path('dir/sub_dir/')

print(p_dir.with_suffix('.text'))
# dir/sub_dir.text

with_suffix()with_name()と同様に、拡張子を変更したファイルを作成したりするのに便利。

stem部分を変更したパスを取得: with_stem()

Python 3.9でwith_stem()メソッドが追加された。

with_stem()は元のパスのstem属性(拡張子を除いたファイル名)を変更したPathオブジェクトを返す。

p_file = Path('dir/sub_dir/file.txt')

print(p_file.with_stem('new_file'))
# dir/sub_dir/new_file.txt

print(type(p_file.with_stem('new_file')))
# <class 'pathlib.PosixPath'>

拡張子なしのファイルやディレクトリの場合、ファイル名・ディレクトリ名全体が変更される。

p_dir = Path('dir/sub_dir/')

print(p_dir.with_stem('new_dir'))
# dir/new_dir

関連カテゴリー

関連記事