note.nkmk.me

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

Date: 2018-10-01 / tags: Python, ファイル操作

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

pathlibはPython3.4から追加されたモジュール。ファイルやディレクトリのパスをオブジェクトとして操作できる。標準ライブラリに含まれているので追加のインストールは不要(importは必要)。

ここでは以下の内容について説明する。

  • Pathオブジェクトの基礎
  • ファイル名(basename)、ディレクトリ名を取得: name, stem
  • 拡張子を取得: suffix
  • 親ディレクトリを取得: parent, parents[]
  • 同じディレクトリの別のファイルを取得: with_name()
  • 拡張子を変更したパスを取得: with_suffix()

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

慣れると文字列ベースのos.pathよりもオブジェクトベースのpathlibモジュールのほうが使いやすい。

pathlibの基本的な使い方については以下の記事を参照。

以下のようなファイル・ディレクトリ(フォルダ)構成を例とする。

temp
├── dir
│   └── sub_dir
│       └── file2.txt
└── file.txt
スポンサーリンク

Pathオブジェクトの基礎

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

import pathlib

p_file = pathlib.Path('temp/file.txt')

print(p_file)
# temp/file.txt

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

OSによってPosixPathまたはWindowsPathのインスタンスとなる。

文字列に変換したい場合はstr()を使う。

print(str(p_file))
# temp/file.txt

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

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

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

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

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'>

ディレクトリを指すPathオブジェクトの場合は、namestemどちらも末尾(最下層)のディレクトリ名の文字列を返す。

p_dir = pathlib.Path('temp/dir/')

print(p_dir)
# temp/dir

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

print(p_dir.name)
# dir

print(p_dir.stem)
# dir

拡張子を取得: suffix

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

print(p_file.suffix)
# .txt

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

ディレクトリの場合は空文字列。

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:])
# 

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

サブディレクトリにあるファイルを示すPathオブジェクトを例とする。

p_sub = pathlib.Path('temp/dir/sub_dir/file2.txt')

print(p_sub)
# temp/dir/sub_dir/file2.txt

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

print(p_sub.parent)
# temp/dir/sub_dir

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

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

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

print(p_sub.parents[1])
# temp/dir

print(p_sub.parents[2])
# temp

print(p_sub.parents[3])
# .

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

# print(p_sub.parents[4])
# IndexError: 4

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

p_abs = p_sub.resolve()

print(p_abs)
# /Users/mbp/Documents/my-project/python-snippets/notebook/temp/dir/sub_dir/file2.txt

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

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

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

..を含む場合の注意

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

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

p_file = pathlib.Path('temp/file.txt')

print(p_file)
# temp/file.txt

p_file_rel = pathlib.Path('temp/dir/sub_dir/../../file.txt')

print(p_file_rel)
# temp/dir/sub_dir/../../file.txt

print(p_file.samefile(p_file_rel))
# True

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

print(p_file.parents[0])
# temp

print(p_file.parents[1])
# .

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

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

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

print(p_file_rel.parents[3])
# temp/dir

parentおよびparents[]は純粋な字句操作であるため、元が同じファイルやディレクトリを示していても、..が含まれている場合と含まれていない場合で結果が異なる。

resolve()で絶対パスに変換すると..は除去されるので、..を含んでいる場合は、まずresolve()で絶対パスに変換してからparentparents[]を使ったほうが間違いを防げる。

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

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

print(p_file_rel.resolve().relative_to(p_file_rel.cwd()))
# temp/file.txt

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

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

ファイルを指すPathオブジェクトを例とする。

p_file = pathlib.Path('temp/file.txt')

with_name()を使うと、現在のパスのname属性を変更したPathオブジェクトが返される。引数に新たなname属性を文字列で指定する。

ファイルを指すPathオブジェクトに対してwith_name()を使いファイル名を引数に指定すると、同じディレクトリ(同じ階層)の別のファイルを指すPathオブジェクトが取得できる。

print(p_file.with_name('file_new.txt'))
# temp/file_new.txt

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

ディレクトリを示すPathオブジェクトの場合も同様。

p_dir = pathlib.Path('temp/dir/')

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

print(p_dir.with_name('dir_new'))
# temp/dir_new

print(p_dir.with_name('file_new.txt'))
# temp/file_new.txt

with_name()は同じディレクトリに新たなファイルやディレクトリを作成するのに便利。

新たなファイル名やディレクトリ名を指定してまだ存在しないパスに対するPathオブジェクトを取得し、そのPathオブジェクトを使って新規ファイルや新規ディレクトリを作成できる。

空のファイルを作成するtouch()メソッドを使った例は以下の通り。

p_file.with_name('file_new.txt').touch()

print(p_file.with_name('file_new.txt').exists())
# True

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

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

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

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

print(p_file.with_suffix('.text'))
# temp/file.text

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

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

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

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

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事