『退屈なことはPythonのやらせよう』その7(9章: ファイル管理)

Python初心者です。『退屈なことはPythonにやらせよう』を走っています。

7回目の今回は、9章をやっていきます~。

前回の記事はこちら↓

今回の目次

9章のファイル操作では、以下のようなことを学びました。

  • shutilモジュールの使いかた
  • send2trashモジュール
  • ディレクトリツリーの歩き方
  • zipファイルの操作


演習プロジェクト1:選択コピー

課題の内容

ディレクトリツリーを渡り歩いて、「.pdf」や「.jpg」などの特定の拡張子を持つファイルを見つける。

その後、見つけたファイルをすべて新しいフォルダにコピーする。

具体的な機能

  • コマンドプロンプトにて、<拡張子>と<ファイルパス>を渡せるようにする
  • 渡された<拡張子>を正規表現に変換
  • 渡されたファイルパスのディレクトリに含まれるファイルをすべてチェックする
  • 拡張子がマッチしたファイルを、新規に作成したフォルダにコピー

以下が実際のコード

#! python3
# check09-1.py  -  ディレクトリツリーを渡り歩いて、.pdfや.jpgなどの特定の拡張子を持つファイルを見つけ
#                  新しいフォルダにコピーするプログラム
# Usage
# py check09_1.py <拡張子> <ファイルパス>

#ディレクトリツリーを渡り歩く
#.pdf, .jpgなどのファイルを見つける
# 新しいディレクトリを作り、そこに見つけたファイルを保存する

import os, sys, shutil, re
new_dir = os.mkdir("見つけたファイルの格納用のフォルダ(探索するフォルダと同じかそれより上の階層)")
regex_ext = re.compile(sys.argv[1])

for foldername, subfolders, filenames in os.walk(sys.argv[2]):
    print("The current folder is " + foldername)

    for subfolder in subfolders:
        print("SUBFOLDER OF " + foldername + ": " +subfolder)
    
    for filename in filenames:
        print("FILE INSIDE " + foldername + ": " + filename)
        mo = regex_ext.search(os.path.join(foldername,filename))
        if mo:
            shutil.copy(os.path.join(foldername,filename), ("見つけたファイルの格納用のフォルダ(探索するフォルダと同じかそれより上の階層)"))
    print("")

当初は同じ階層に新しいフォルダを作っていたが、既にコピーしたものを再びコピーすることになってしまい、エラーがでた。

try節で例外処理をすることも考えたが、チェックしているよりも上の階層に保存先フォルダを作るほうが楽そうだったので、今回は対策していない。

os.walk(path)では”generator object”なるものが得られる

os.walk(path)を用いて取得したものを、

  • foldername
  • subfolders
  • filenames

に格納して、使用しています。


演習プロジェクト2:巨大なファイルを探す

課題の内容

ディレクトリツリーを渡り歩いて、大きなサイズのファイルを見つけ、画面に出力するプログラムです。

今回の課題用のコードでは、コマンドライン上で

py check09_2.py <path> <size>

と入力することで、指定されたパスのディレクトリの中から、指定されたサイズ以上のファイルの絶対パスを出力するようにしました。

os.walk()の型を保存しておくと、フォルダを渡り歩いて見つける系のコードを書くのはすごく便利ですね。

#! python3
# check09_2.py  -  ディレクトリツリーを渡りあるいて、100MB以上のファイルを見つける。
# 見つけたファイルは絶対パスを画面に表示する
# Usage
# py check09_2.py <path> <size>

import os, sys

for foldername, subfolders, filenames in os.walk(sys.argv[1]):

    for filename in filenames:
        #print("FILE INSIDE " + foldername + ": " + filename)
        if os.path.getsize(os.path.join(foldername, filename)) >= int(sys.argv[2]):
            print(os.path.join(foldername, filename))

前回の章でsysモジュールなどを学んだので、コマンドプロンプトから引数をもらうコードを積極的に書いています。

特に突っかかるところもなくできました。

原文のほうでもos.walk()のテンプレが見れるので、必要な方はこちらのリンクへどうぞ(英語の原文ページにとびます。)


演習プロジェクト3:連番の飛びを埋める

課題の内容

ひとつのフォルダの中にある、連番ファイル(~~001.txt, ~~002.txt…)の中から、

  • 連番が飛んでいる場所を発見
  • 連番が飛んだ分をずらしてファイル名を変更

するコードを書きます。

それにプラスして、抜けのある連番ファイルのあるフォルダを作成するコードを書きます。

抜けのある連番ファイルの作成

まずは操作するファイルのまとまったフォルダを作るコードです。

#! python3
# check09_3_preparefile.py  -  fileの中に連番のファイルを作成する
# spam folderの中に、spam001 ~ spam020のテキストファイルを作る
# 3, 7, 13のファイルだけ作らない
# spamフォルダはコマンドラインで先に準備しておく

import os

path = os.getcwd()
file_path = os.path.join(path, "spam")

for i in range(1,21):
    if i == 3 or i == 7 or i ==13:
        continue
    created_file = open(os.path.join(file_path, "spam{:03}.txt".format(i)) , "w")
    created_file.write("neko{}".format(i))
    created_file.close()

コマンドライン引数を使って抜けの番号を指定しても良かったのですが、特に必要なさそうだったので省略してしまいました。

今回は、3, 7, 13の抜けた状態の.txtファイルを作っています。

“spam{:03}.txt”.format(i)

とすることで、forループの i を数値として受け取り、3桁の数字を入れられるようにしています。

ファイルを格納するフォルダはコマンドラインのほうで前もって作成するコードにしました。

連番の抜けを判別、抜けを埋めるようにファイル名をずらす

連番の抜けを判定するコードと、抜けを埋めるファイル名をずらすコードを書いていきます。

#! python3
# check09_3.py  -  フォルダ中で、連番ファイルの抜けをみつけ、連番が飛んでいる箇所を見つける。
#                  番号の飛びを埋めるように、後に続くファイルの名前を変更する。
# Usage
# py check09_3.py <path> <pre>

import os, sys, re, shutil

#ファイル名を取得
dir_file = sys.argv[1]
pre = sys.argv[2]
file_dict = {}

file_list = os.listdir(dir_file)

for i in range(0, len(file_list)):
    file_dict[file_list[i]] = pre + "{:03}.txt".format(i+1)

#index的にはあるはずなのに、ファイル名として存在しないファイルを探す
def find_missing_file():
    for fname in file_dict.values():
        regex = re.compile(fname)
        mo = regex.search(", ".join(file_dict.keys()))
        if not mo:
            print(fname)


#元のファイル名から、連番の番号にする
def change_fname(change = False):
    if change == True:
        for k, v in file_dict.items():
            if k != v:
                print(k,v)
                shutil.move(os.path.join(dir_file, k), os.path.join(dir_file, v))
        
find_missing_file()

print("Do you want to change file name?:  y or [n]")
option = str(input())
if option == "y":
    change = True
else:
    change = False
change_fname(change)

コマンドラインにて、

py check09_3.py <path> <連番前のファイル名>

とすると使うことができます。

デフォルトでは連番の抜けをチェックする関数だけが動きます。

その後、ファイル名を変更するかどうか質問し、「y」と入力した場合のみファイル名を変更できる形にしました。


感想

課題の3つ目でめちゃくちゃ時間がかかってしまいました。

ちょっと分からないからといって、コードを書くのを止めてはいけませんね…

コーディングから逃げるな!

次回はデバックの章です~。

次の記事はこちら↓

『退屈なことはPythonのやらせよう』その7(9章: ファイル管理)” に対して4件のコメントがあります。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です