CHAPTER 5 ・ ファイルとテキストの実践 / Lesson 18

パスを安全に扱う(pathlib)

フォルダをたどって対象ファイルを探し、名前や拡張子を取り出す。文字のつなぎ合わせより、安全な方法です。

目安 18分 ・ インストール不要

このレッスンが終わったら

「パスは文字ではなく Path で持つ。glob で探し、stem や suffix で名前を分けられる」と言える。

STEP 1

こんな場面、ありませんか?

invoices フォルダに請求書のファイルが入っています。「誰の請求書か」が分かるように、ファイル名から拡張子(.txt)を外した名前を一覧にしたい、という場面を考えます。基礎編の書き方(基礎編 Lesson 13 の os と、文字の操作)では、こうなります。

基礎編の書き方:文字をつなげて、split で分けるサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

動きはしますが、危ない点が2つあります。

  • 文字で "/" をつなぐやり方は、Windows(区切りが \)とそれ以外で食い違います。
  • split(".")[0] は、名前の途中に . があると、最後の行のように 2025 だけが取れてしまいます。

さらに、Windows のパスをそのまま文字として書くと、こんなエラーにもなります。

わざと間違える:Windows のパスを、そのまま書いたコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

\U が「特別な文字」と読み取られるためです(\n は改行、\t はタブになります)。パスを文字で書くと、こうしたつまずきが続きます。これをまとめて片づけてくれるのが、この回の主役の pathlib です。

STEP 2

Path で書いてみる

同じことを、pathlib の Path で書きます。「実行」を押してください。

Path で、名前と拡張子を取り出すサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

.txt のファイルだけが対象になり、最後の行も 2025.10_invoice_tanaka と、正しく取れました。フォルダに / をつなぐこともできます。ファイルの中身を読むのも、1行です。

/ でつなぐ、存在を調べる、読むサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

/ は割り算ではなく、「パスをつなぐ記号」です。Windows で動かせば、自動で \ が使われます。

STEP 3

仕組みを読む

Path は、パスをただの文字ではなく、部品を持ったものとして扱います。1つのファイルのパスは、次の部品に分かれます。

Path("invoices/invoice_tanaka.txt") の部品
invoices.parent(入っているフォルダ)
/区切り
invoice_tanaka.stem(拡張子を除いた名前)
.txt.suffix(拡張子)

invoice_tanaka.txt の部分(.stem と .suffix を合わせたもの)は .name で取れます。先ほどのコードを、1行ずつ日本語にします。

Path のコードを、1行ずつ日本語にしてみる
from pathlib import Pathpathlib から、Path という道具を借りる
folder = Path("invoices")invoices フォルダを、Path として持つ(文字ではない)
for p in sorted(folder.glob("*.txt")):フォルダの中から、.txt で終わるものを探し、名前順に1つずつ「p」と呼ぶ
print(p, "→", p.stem)パス全体と、拡張子を除いた名前を表示する

日本語にすると

invoices フォルダの中から、.txt のファイルを探して、名前順に1つずつ取り出します。パスと、拡張子を除いた名前を表示してください。

*.txt の * は「何でもよい(0文字以上)」という意味の記号で、これを使ってファイルを探す書き方をglob(グロブ)と呼びます。基礎編の endswith(".txt") で選ぶ代わりに、探す段階で絞り込めます。

基礎編(os と文字の操作)

import os

for name in sorted(os.listdir("invoices")):
  if name.endswith(".txt"):
      path = os.path.join("invoices", name)
      print(name.split(".")[0])

pathlib

from pathlib import Path

for p in sorted(Path("invoices").glob("*.txt")):
  print(p.stem)
「探す」「つなぐ」「名前を分ける」が、すべて Path の道具で済みます。

.suffix が返す .txt のような、名前の最後の部分を拡張子と呼びます。ドット(.)を含めた形で返ります。

STEP 4

変えて、試す

① 深いところまで探す。 glob は、指定したフォルダの直下だけを探します。サブフォルダの中まで探すときは rglob を使います。ここでは、年ごと・月ごとのフォルダを作ってから探します。mkdir(parents=True, exist_ok=True) は、「途中のフォルダもまとめて作る。もうあってもエラーにしない」という意味です。

rglob で、サブフォルダの中まで探すコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

直下には1つもないので glob は 0 件、rglob は 2 件です。(as_posix() は、/ 区切りの文字にして表示する指定です。)

② 名前の変更は、まず「予定」を表示する。 ファイル名の変更は、元に戻せないことがあります。いきなり変更せず、変更後の名前を表示するだけにして確かめます。このやり方を試し実行(ドライラン)と呼びます。

変更の予定だけを表示する(まだ変えない)サンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

予定が正しいと確かめられたら、次のように rename を足します。with_name は、フォルダはそのままで、名前の部分だけを入れ替えた新しい Path を作ります。

予定を確かめてから、実際に名前を変えるサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

iterdir() は、フォルダの中身を1つずつ取り出します。

③ ないファイルを読むと? 存在しないパスを read_text で読むと、FileNotFoundError(ファイルが見つかりません)で止まります。読む前に p.exists() で調べるか、try / except(Lesson 09 で学びます)で備えます。

STEP 5

自分で書く

お題① invoices フォルダにあるファイルを、拡張子ごとに数えて、拡張子 件数 の形で表示してください。拡張子の順番は、sorted で並べます。

お題①:拡張子ごとの件数サンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

for p in Path("invoices").iterdir(): で1つずつ取り出し、counts[p.suffix] = counts.get(p.suffix, 0) + 1 と数えます。.get は Lesson 06 で学ぶ「なければ0」の書き方です。

答えの例を見る
from pathlib import Path

counts = {}
for p in Path("invoices").iterdir():
  counts[p.suffix] = counts.get(p.suffix, 0) + 1

for suffix, n in sorted(counts.items()):
  print(suffix, n)

お題② 請求書(.txt)の、名前の変更案を作ります。invoice_tanaka.txt を 2025-10-31_tanaka.txt のように、先頭の invoice_ を日付に替えた名前にして、今の名前 → 新しい名前 の形で表示してください。(名前は変更しません)

お題②:名前の変更案を表示するサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

p.name.replace("invoice_", "2025-10-31_") で、名前の一部を入れ替えられます。print(p.name, "→", new_name) と書きます。

答えの例を見る
from pathlib import Path

for p in sorted(Path("invoices").glob("*.txt")):
  new_name = p.name.replace("invoice_", "2025-10-31_")
  print(p.name, "→", new_name)

お題③ reports/2025-10 というフォルダを作り(途中の reports もまとめて)、その中の memo.txt に「確認済み」と書いて、書いた内容を読み直して表示してください。

お題③:フォルダを作って、書いて、読み直すコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

folder = Path("reports") / "2025-10" を作り、folder.mkdir(parents=True, exist_ok=True)。書くのは (folder / "memo.txt").write_text(...)、読むのは read_text(...) です。

答えの例を見る
from pathlib import Path

folder = Path("reports") / "2025-10"
folder.mkdir(parents=True, exist_ok=True)
memo = folder / "memo.txt"
memo.write_text("確認済み", encoding="utf-8")
print(memo.read_text(encoding="utf-8"))
STEP 6

仕事ならどう使う?

理解チェックPath("invoices/2025.10_tanaka.txt").suffix の結果は、どれでしょう?

理解チェックサブフォルダの中にある .csv も含めて、すべて探したいときは?