Python, pathlibでファイル名・拡張子・親ディレクトリを取得
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
のサブクラスなので、Path
(PosixPath
または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'>
パスの末尾の要素がピリオド.
を含まない、拡張子なしのファイルやディレクトリの場合、name
もstem
どちらも末尾の要素(ファイル名やディレクトリ名)の文字列を返す。
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()
のあとでparent
やparents[]
を使うと間違いを防げる。
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