1. フォルダ名を「手記」、ファイル名を「手記201512」とかでダイアリーフォルダを作る
  2. 「『手記』とかちょっと中二っぽくて恥ずかしいわ/// 全部『日記』に変えよう!」
  3. 三年ぶんのフォルダ、ファイルに加えてファイル内にも手記という言葉が使われていて全部変更するのは手間…

そんなあなたに今日もやってまいりました、「1日1python」のお時間です。(ただし隔週放送)

今回晒すのはそんな事情から書いたスクリプト。指定したディレクトリ(フォルダ)内のすべてのフォルダ名、ファイル名、ファイルの中のテキストを検索置換する。え? いや冒頭のはたとえばの話で、緑さんはそんな中二っぽい名前にはしてませんよ。ハハハ。

まずトップディレクトリ、置換パターン、読み込まなくていいファイル拡張子(画像とかね)を定数チックに指定しとく。

TOP_DIR  = "D:/foo"
PATTERNS = {
            "怪文書"   :"日記",
            "Kaibunsho":"Diary",
            "kaibunsho":"diary"}
EXT      = [".png", ".gif", ".jpg", ".ico"]

次にトップディレクトリ以下すべてのフォルダのパスをリストに格納する。手順は次のような感じ。

  • dirsリストにトップディレクトリのパスを格納
  • dirs内のパスを覗いて、それがフォルダだったらそのパスをdirsに格納
  • ふたたびdirsを参照するとたった今追加したパスがあるので、それを覗いて繰り返す

ループ回数を動的に増やせるってのは試しに書いてみて初めて知った。こりゃ便利だなあ。ヘタを打つと無限ループ入ってぶっ飛ぶけど(一度ぶっ飛ばした)。

import os
dirs = []
dirs.append(TOP_DIR)
i = 0
for directory in dirs:
    files = os.listdir(directory)
    for f in files:
        path = directory + "/" + f
        if os.path.isdir(path):
            dirs.append(path)

そして次のが置換関数と拡張子チェック関数。しかし、プログラミングで遊び始めて数ヶ月になるけれど、いまだにどんなタイミングで関数を作ればいいのかよく分からないな。てきとうに「ここを関数化したら見た目カッコよくなるんじゃね?」みたいな乗りで書いちゃってんだけど。

def replacement(string):
    for key, value in PATTERNS.items():
        string = string.replace(key, value)
    return string
def ext_check(path):
    root, ext = os.path.splitext(path)
    if ext in EXT:
        return True
    else:
        return False

最後に、さきほど作ったリストを下層から順に開いて処理していく。上層から開いたらせっかく作ったパスリストがパァだ(一度やらかした)。手順は次のような感じ。

  • dirsのパスをひとつずつ展開する
  • EXTリストに登録した拡張子のファイルだったらスルーする
  • フォルダだったらPATTERNSに従いリネームして次へ
  • ファイルだったらリネームのうえ中身を読み込んで検索置換して上書き
dirs.reverse()
for directory in dirs:
    files = os.listdir(directory)
    for f in files:
        if not ext_check(directory + "/" + f):
            try:
                g = replacement(f)
                os.rename(directory + "/" + f, directory + "/" + g)
                path = directory + "/" + g
                if os.path.isfile(path):
                    h = open(path, encoding="utf-8")
                    data = h.read()
                    data = replacement(data)
                    h.close()
                    h = open(path, "w", encoding="utf-8")
                    h.write(data)
                    h.close()
            except:
                print(path)

あんまり入れ子構造が深くなるのは望むところではないんだけどねえ。どうすりゃいいんだろ。

今回の咬まれポイントはひとつだけ。

# os.rename()の引数はどちらもパスにすること
os.rename("D:/foo/bar", "D:/foo/baz")

今回は構文とか関数の使い方よりも、全体のロジックを考えるのに時間を使ったかな。

以下の記事からリンクされています