パスを、そこに含まれる拡張子とそれ以外の部分に分割したいことはよくある。これを行うにはsplitext関数やPath.stem/Path.suffix/Path.parent/Path.suffixes属性が使える。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
# パスを拡張子とそれ以外の部分に分割する
from os.path import splitext
path = '/dir0/dir1/somefile.txt'
root, ext = splitext(path)
print(f'root: {root}, ext: {ext}') # root: /dir0/dir1/somefile, ext: .txt
# Pathオブジェクトではparent属性とstem属性を組み合わせる
from pathlib import Path
path = Path(path)
print(f'path.stem: {path.stem}') # path.stem: somefile
print(f'path.suffix: {path.suffix}') # path.suffix: .txt
print(f'path.parent: {path.parent}') # path.parent: /dir0/dir1
root = Path(f'{path.parent}/{path.stem}')
print(f'root: {root}, ext: {path.suffix}') # root: /dir0/dir1/somefile, ext: .txt
# 拡張子が連続している場合にそれらを拡張子として分割するには
path = '/dir0/dir1/somefile.tar.gz'
root, ext = splitext(path)
print(f'root: {root}, ext: {ext}') # root: /dir0/dir1/somefile.tar, ext: .gz
def mysplitext(path):
root, ext = splitext(path)
exts = ext
while ext != '':
root, ext = splitext(root)
exts = ext + exts
return root, exts
root, ext = mysplitext(path)
print(f'root: {root}, ext: {ext}') # root: /dir0/dir1/somefile, ext: .tar.gz
path = Path(path) # root: /dir0/dir1/somefile, ext: .tar.gz
root = Path(f'{path.parent}/{path.stem}')
ext = path.suffix
print(f'root: {root}, ext: {ext}') # root: /dir0/dir1/somefile.tar, ext: .gz
print(path.suffixes) # ['.tar', '.gz']
def mysplitext(path):
parent = str(path.parent)
parent = '' if parent == '.' else parent
tmp = path.stem
while True:
stem = Path(tmp).stem
if stem == tmp:
break
tmp = stem
suffixes = ''.join(path.suffixes)
root = f'{parent}/{stem}' if parent else stem
return root, suffixes
root, ext = mysplitext(path)
print(f'root: {root}, ext: {ext}') # root: /dir0/dir1/somefile, ext: .tar.gz
os.pathモジュールには渡されたパスを、拡張子とそれ以外の部分に分割するsplitext関数がある(引数には文字列、Pathオブジェクトなどを指定できる)。splitext関数はパスを与えるとそれを「(拡張子以外, 拡張子)」というタプルに含めて返送する。
以下に例を示す。
from os.path import splitext
path = '/dir0/dir1/somefile.txt'
root, ext = splitext(path)
print(f'root: {root}, ext: {ext}') # root: /dir0/dir1/somefile, ext: .txt
この例ではパスとして「'/dir0/dir1/somefile.txt'」をsplitext関数に渡している。そのため、拡張子とそれ以外に分割された「('/dir0/dir1/somefile', '.txt')」というタプルが返送されていることが分かる。
一方、pathlibモジュールのPathクラスにはstem属性とsuffix属性がある。stem属性はパスを構成する最終要素の拡張子よりも前の部分を表し、suffix属性は拡張子を表す。そのため、単にこれら2つを結合するだけではsplitext関数とは異なる結果になることがある。
以下に例を示す。
from pathlib import Path
path = Path(path) # '/dir0/dir1/somefile.txt'
print(f'path.stem: {path.stem}') # path.stem: somefile
print(f'path.suffix: {path.suffix}') # path.suffix: .txt
変数pathには先ほどの内容('/dir0/dir1/somefile.txt')をPathクラスのオブジェクトにしたものが代入されている。そして、そのstem属性はパスを構成する最終要素(somefile.txt)から拡張子を取り除いたものになっている。これに対して、suffix属性は拡張子になっている。
splitext関数では「パスの構成要素全てから拡張子を除いたもの」と「拡張子」が得られていたので、Path.stem/Path.suffix属性とsplitext関数の戻り値が完全に対応しているわけではないことには注意しよう。同様な結果を得るには、パスを構成する要素のうち、最終要素よりも前の部分を表すPath.parent属性を使える。
print(f'path.parent: {path.parent}') # path.parent: /dir0/dir1
よって、splitext関数と同様な結果を得るには以下のようにすればよい。
root = Path(f'{path.parent}/{path.stem}')
print(f'root: {root}, ext: {path.suffix}') # root: /dir0/dir1/somefile, ext: .txt
拡張子が複数ある場合にもsplitext関数やPath.parent/Path.stem/Path.suffix属性を使うだけで、複数の拡張子とそれ以外の部分に分割できるかというとそうではない。
以下はsplitext関数を使った場合の例だ。パスには「'/dir0/dir1/somefile.tar.gz'」を指定している。
path = '/dir0/dir1/somefile.tar.gz'
root, ext = splitext(path)
print(f'root: {root}, ext: {ext}') # root: /dir0/dir1/somefile.tar, ext: .gz
このときには拡張子として認識されるのは最後の「.gz」だけだ。これはPathクラスのsuffix属性についても同様だ。
path = Path(path)
root = Path(f'{path.parent}/{path.stem}')
ext = path.suffix
print(f'root: {root}, ext: {ext}') # root: /dir0/dir1/somefile.tar, ext: .gz
このパスを「.tar.gz」とそれ以外の部分に分割したいとしよう。
以下はsplitext関数を使ってこれを行う関数の例だ。
def mysplitext(path):
root, ext = splitext(path)
exts = ext
while ext != '':
root, ext = splitext(root)
exts = ext + exts
return root, exts
基本的な考え方は、拡張子とそれ以外の部分に分割できる間はsplitext関数を呼び続けて、取り出した拡張子を結合していき、最後にその他の部分と結合された拡張子を返送するというものだ。
実行結果を以下に示す。
root, ext = mysplitext(path)
print(f'root: {root}, ext: {ext}') # root: /dir0/dir1/somefile, ext: .tar.gz
2つある拡張子(.tar.gz)とそれ以外に分割できたことが確認できる。
一方、Pathクラスを使用する場合には、suffixes属性という便利な属性がある。
print(path.suffixes) # ['.tar', '.gz']
この属性はパスを構成する最終要素に含まれる拡張子を要素とするリストを表す。これを「''.join(path.suffixes)」のようにすれば拡張子('.tar.gz')を得られる。このこととstem属性が(末尾の)拡張子よりも前の部分を表すことを組み合わせると、以下のようなコードが考えられる。
def mysplitext(path):
parent = str(path.parent)
parent = '' if parent == '.' else parent
tmp = path.stem
while True:
stem = Path(tmp).stem
if stem == tmp:
break
tmp = stem
suffixes = ''.join(path.suffixes)
root = f'{parent}/{stem}' if parent else stem
return root, suffixes
基本的な考え方は、stem属性を何度か呼び出して「somefile.tar.gz」→「somefile.tar」→「somefile」のように最終的に拡張子以外の部分を得て、それと「''.join(path.suffixes)」の結果を結合しようというものだ(関数の前後にある細々とした部分はsplitext関数で得られる結果と同じ結果が得られるようにするためのもの)。もっと別な実装もあるだろうが、ここではこのようにしている(例えば、suffix属性が空になるまでループをしていく方法)。
実行結果を以下に示す。
root, ext = mysplitext(path)
print(f'root: {root}, ext: {ext}') # root: /dir0/dir1/somefile, ext: .tar.gz
こちらでも2つの拡張子とそれ以外の部分に分割できることが確認できた。
Copyright© Digital Advantage Corp. All Rights Reserved.