今シリーズは簡単なことはpythonでやってしまおう!!という精神の記事です。
今回は「python自動化」シリーズその1
自動化処理はプログラミングの醍醐味!!
今回はpythonでファイル名の一括変更を実装していきます。
ファイルのリネームを行うには、
パス一覧取得→ファイル名の取得→リネーム→元のパスに連結という流れになります。
では解説していきます。
準備
ファイル構造は以下を想定して解説していきます。
└── testdir
├── a.jpg
├── b.jpg
├── c.jpg
├── d.jpg
└── e.jpg
注意
ファイル名の変更や書き換えは失敗しても大丈夫なようにデータのバックアップをとっておきましょう。
globモジュール(パス一覧取得)
今回はglobモジュールを使用します。
globモジュールはファイルのパスを取得してくれるモジュールです。
glob.glob(‘./*’)で、カレントディレクトリ内のファイル名とディレクトリ名のリストが取得できる。引数は絶対パスでも相対パスでもOKです。
今回の例でファイル名を取得するには以下のようになります。
import glob
print(glob.glob('./testdir/*'))
# => ['./testdir/a.jpg', './testdir/b.jpg', './testdir/c.jpg', './testdir/d.jpg', './testdir/e.jpg']
*はワイルドカードといい、全ての文字列をパターンマッチしてくれます。
例えばjpgファイルのみを取得したい場合はglob.glob(‘./testdir/*.jpg’)と書きます。
パターンマッチには他にも多くありますが、globでは以下のパターンマッチが使えます。
- *: すべてのものにマッチする
- ?: 任意の一文字にマッチする
- [abc]: a, b, cのいずれかの一文字にマッチする
- [!abc]: a, b, c以外の一文字にマッチする
osモジュール(ファイル名取得・リネーム・連結)
osモジュールは、OSに依存しているさまざまな機能を利用するためのモジュールです。
ファイル名の取得
ファイル名をリネームするにはパスからファイル名のみを取得する必要があります。
ファイル名の取得にはos.path.basename()を使用します。
使用例は以下の通りです。
import os
import glob
path = "./testdir"
files = glob.glob(path + '/*')
for f in files:
print(os.path.basename(f))
# => ['a.jpg', 'b.jpg', 'c.jpg', 'd.jpg', 'e.jpg']
ファイル名だけが抽出できたと思います。
ファイルのリネーム
os.rename()関数を使用して、ファイル名を変更していきます。
使用例は以下の通り
import os
os.rename('./testdir/a.jpg', './testdir/a_000.jpg')
これでa.jpgがa_000.jpgにリネームされます。
パスとの連結
ファイル名の変更後も元のディレクトリに保存するためには、元のパスに新しいファイルを連結する必要があります。
パスの連結にはos.path.join()関数を使用します。
使用例は以下の通りです。
import os
print(os.path.join("/A/B/C", "file.py"))
# => ['/A/B/C/file.py']
ファイルの前に文字列や連番を追加
これまで説明してきた処理を使って、ファイル名を一括変更することができます。
一番基本の共通文字列をファイル名の前に一括で追加してみます。
import os
import glob
path = "./testdir"
files = glob.glob(path + '/*')
str = "sample_"#追加する文字列
for f in files:
os.rename(f, os.path.join(path, str + os.path.basename(f)))#パスfをpath+(str+fのファイル名部分)に変更
以下のようにファイル名が変更されたと思います。
└── testdir
├── sample_a.jpg
├── sample_b.jpg
├── sample_c.jpg
├── sample_d.jpg
└── sample_e.jpg
これだとあまり使い道が分からないので、ファイルに連番をつけてみます。
ファイル名の前に001,002,・・・010,・・・999というように連番をつけてみます。
連番の文字列を作成するにはstr.format()を使用します。
使用例は以下のようになります。
# 3を2桁でゼロ埋め
print('{0:02d}'.format(3))
# => 03
# 4と6をそれぞれ3桁と4桁でゼロ埋め
print('{0:03d}, {1:04d}'.format(4, 6))#複数の場合{0:-},{1:-}となる
# => 004, 0006
連番を追加する場合はfor文のところをenumerate()で回します。
enumerate()はリストのインデックスと中身(データ)をセットで取得することができます。
実装は以下の通りです。
import os
import glob
path = "./testdir"
files = glob.glob(path + '/*')
for i, f in enumerate(files):#iがインデックス,fがデータ
os.rename(f, os.path.join(path, '{0:03d}'.format(i) + '_' + os.path.basename(f)))#インデックスを3桁で0埋め
以下のようにファイル名が変更されたと思います。
└── testdir
├── 000_a.jpg
├── 001_b.jpg
├── 002_c.jpg
├── 003_d.jpg
└── 004_e.jpg
連番を001からにしたい場合は、enumerate(files, 1)としてあげます(i+1でもいい)
連番はファイルが扱いやすくなりますし、普段から使う場面もあるのではないでしょうか?
ファイルの後ろに文字列や連番を追加
ファイル名の後ろに連番をつけてみます。
その場合、パス「./testdir/a.jpg」を「./testdir/a」と「.jpg」に分割してから、「./testdir/a_001」+「.jpg」のように連結します。
ファイル名を拡張子で分割するにはos.path.splittext()を使用します。
使用例は以下の通りです。
import os
path = "./testdir/a.jpg"
ftitle, ftext = os.path.splitext(".testdir/a.jpg")
print('ftitle:%s' % ftitle)
print('ftitle:%s' % ftext)
# => .testdir/a
# => .jpg
ファイル名の後ろに連番をつける処理は以下の通りになります。
import os
import glob
for i, f in enumerate(files):
ftitle, fext = os.path.splitext(f)#パスを拡張子のところで分割
os.rename(f, ftitle + '_' + '{0:03d}'.format(i) + fext)
以下のようにファイル名が変更されたと思います。
└── testdir
├── a_000.jpg
├── b_001.jpg
├── c_002.jpg
├── d_003.jpg
└── e_004.jpg
リストと組み合わせてファイル名を変更
一通りのファイル名の一括変更を学ぶことができました。
これを応用してリストと組み合わせてファイル名を変更してみます。
このようなリストを用意します。
ファイル名をリストの中身に変えていきます。
実装は以下の通り。
import os
import glob
name_list = ["apple", "banana", "cherry", "durian", "eggplant"]
path = "./testdir"
files = glob.glob(path + '/*')
for i, f in enumerate(files):
os.rename(f, path + '/' + name_list[i]+".jpg")
└── testdir
├── apple.jpg
├── banana.jpg
├── cherry.jpg
├── durian.jpg
└── eggplant.jpg
リストと組み合わせれば、このような応用もできるわけですね。
膨大の数のファイル名を一つずつ変えるのは相当な労力が必要です。
このようにプログラミングが少しできるだけで、今までの仕事を効率化できるかもしれません。