ある特定のディレクトリに
"ABC1.jpg"
"ABC2.jpg"
"ABC3.jpg"
"ABC4.jpg"
"ABC5.jpg"
とあったとします。
この中で1ファイルを削除し、ファイル名を付け直すにはどうしたら良いのでしょうか?
例えば、"ABC3.jpg"を削除。
"ABC1.jpg"
"ABC2.jpg"
"ABC4.jpg"
"ABC5.jpg"
となったものを、
"ABC1.jpg"
"ABC2.jpg"
"ABC3.jpg" ("ABC4.jpg"からリネーム)
"ABC4.jpg" ("ABC5.jpg"からリネーム)
ファイル名は"XXX0.xxx"で「X]は固定で[0]の部分1文字だけ違うものです。
アドバイス頂きたいです。お願いします。
ファイルの削除は unlink でやるとして、リネームは次のような感じでどうでしょう。
$path = './testimages/' ; // ディレクトリのパス $files = scandir($path) ; $pre = 'ABC' ; // ファイル名の前部 $pro = '.jpg' ; // ファイル名の後部 for ($i = 0, $n = 1; $i < count($files); ++$i, ++$n) { // $n : ファイル名に含まれる数字 while(is_dir($path.$files[$i])) array_shift($files) ; // ディレクトリは処理しない if ($files[$i] !== $pre.$n.$pro) rename($path.$files[$i], $path.$pre.$n.$pro) ; // 連続していなければリネーム }
ファイルの削除は unlink でやるとして、リネームは次のような感じでどうでしょう。
$path = './testimages/' ; // ディレクトリのパス $files = scandir($path) ; $pre = 'ABC' ; // ファイル名の前部 $pro = '.jpg' ; // ファイル名の後部 for ($i = 0, $n = 1; $i < count($files); ++$i, ++$n) { // $n : ファイル名に含まれる数字 while(is_dir($path.$files[$i])) array_shift($files) ; // ディレクトリは処理しない if ($files[$i] !== $pre.$n.$pro) rename($path.$files[$i], $path.$pre.$n.$pro) ; // 連続していなければリネーム }
ありがとうございます。
書いていただいたソースを参考にやってみたのですが、while(is_dir・・・の部分で
Fatal error: Maximum execution time of 30 seconds exceededと出てタイムアウト
してしまいます。
ちなみに頂きましたコードの前に該当ファイルは削除しています。
ヒントをいただけると助かります。
こんな考え方でどうでしょうか。
・ディレクトリの中のファイル名のリストを得る ・リストのファイル名をひとつづつ見ながら以下を行う ・ファイル名の先頭3文字と、残りの部分を得る ・ひとつ前のファイル名の先頭3文字と、今見ているファイル名の3文字を比較し、 違っていたら連番のカウンタを 1 にもどす 同じだったら連番のカウンタを 1 増やす ・今見ているファイル名の3文字と、連番のカウンタの値と、残りの部分を連結して新しいファイル名を決める ・今見ているファイル名のファイルの名前を新しいファイル名に変更する ・今見ているファイル名を、ひとつ前のファイル名を保持する変数にとっておく
ファイル名が XXX0.xxx の形式で、
X が半角英数字(マルチバイトじゃない)で、
0 は1文字の半角数字という前提です。
あと連番のカウンタの初期化とか、ファイル名を保持する変数の初期化をしておくとかもいるかな。
以下はテキトーなサンプルコードです。
ディレクトリを img としています。
<?php $directory = "img"; $list = glob("$directory/*"); $seq = 1; $prev_filename = ""; foreach($list as $path){ $filename = basename($path); $prefix = substr($filename,0,3); $suffix = substr($filename,4); $prev_prefix = substr($prev_filename,0,3); if($prev_prefix != $prefix){ $seq = 1; }else{ $seq += 1; } $new_filename = $prefix.$seq.$suffix; $before = $directory."/".$filename; $after = $directory."/".$new_filename; echo "$before -> $after\n"; rename($before, $after); $prev_filename = $filename; } /* substr で文字の位置を指定は 0 始まり ABC1.jpg 01234567 0文字目から3文字 ABC 3文字目から1文字 1 4文字名以降 .jpg */ ?>
URLはサンプルで使った関数のマニュアルです。
http://www.php.net/manual/ja/function.glob.php
http://www.php.net/manual/ja/function.basename.php
ありがとうございます。
参考にさせて頂きます。
実際に動いて確認できるよう簡単なコードを書いてみました。
ディレクトリの内容をチェックボックスで表示させて、削除するものをチェックするというものです。
チェックボックスにしたことで複数の削除が可能になっています。
本体はdel_rename()です。その他は付随するもので重要ではありません。
$path = "ある特定のディレクトリ"; $glob = scandir($path); $list = validate($_POST['delist'], $glob); if ($list) { del_rename($path, $glob, $list); $glob = scandir($path); } echo form('delist',$glob);
$pathには"ある特定のディレクトリ"へのパスを指定します。このディレクトリで削除やファイル名の付け直しをするのでphpから書き込み権限を与えておく必要があります。
chmod o+w "ある特定のディレクトリ" のような。
scandir()は$pathで指定されたディレクトリ内の名前を配列にして返します。値は名前だけですので$pathが無いと実際のファイルにはアクセスできないので注意が必要です。それだと面倒だというのならフルパス名にすることも可能です。
validate()は$_POST['delist']に値が設定されていると$globに対応する値を返します。
最初は値が設定されていないので空の配列が返されform()からチェックボックスのformが出力されます。
formでチェックボックスがいくつかチェックされるとvalidate()はそのファイル名(削除する)の配列を返しますのでdel_rename()が実行されます。確認のため再度scandir()することで削除、リネームがされたことが次のform()で表示されるので確認できます。
/* php5なら必要ない。 */ function scandir($directory , $sorting_order=0) { $dh = opendir($directory); $list = array(); while ($file_name = readdir($dh)) { if ($file_name == '.' || $file_name == '..') continue; else $list[] = $file_name; } closedir($dh); if ($order) rsort($list); else sort($list); return $list; }
php5ならscandir()はあるので必要ありません。逆にエラーになります。
配列の要素が名前だけでは面倒だというのなら
else $list[] = $file_name;
を
else $list[] = $directory.'/'.$file_name;
とすればokです。多分。
/* $globを元にcheckboxのformを生成 */ function form($name,$glob) { $continue = ''; foreach ($glob as $id=>$file) $continue .= '<input type="checkbox" name="'.$name.'[]" value="'.$id.'">'.$file.'<br>'; $continue .= '<input type="submit" name="send" value="送信">'; return '<form action="'.$_SERVER['PHP_SELF'].'" method="post">'.$continue.'</form>'; }
checkboxのformを生成し、その文字列を返します。checkboxで重要なことはvalueとしてファイル名ではなく、単なる数字にしていることです。そうすることで受け取る側でその値の信憑性をチェックすることが可能になることです。
function validate($env, $glob) { $list = array(); if ($env) foreach ($env as $id) if (isset($glob[$id])) $list[] = $glob[$id]; return $list; }
受け取った値が$globに対応する値の配列を返します。
function del_rename($path, $glob, $list) { $_glob = $glob; foreach ($list as $file) { unlink($path.'/'.$file); array_splice($_glob, array_search($file, $_glob), 1); } foreach ($_glob as $id=>$file) if ($file !== $glob[$id]) { // echo 'rename('.$path.'/'.$file.', '.$path.'/'.$glob[$id].')<br>'; rename($path.'/'.$file, $path.'/'.$glob[$id]); } }
$globは単なるファイル名なので削除、リネームするにはそのディレクトリーの$pathが必要です。$listは削除するファイル名の配列です。
まずリネームのために$globをコピーした$_globを作ります。最初のforeachで削除し、$_globからもarray_splice()を使ってその部分を削除します(全て削除してからscandirするという手もありますが)。
この部分が終了した時点で$globには削除する前の状態が、$_globは削除された状態になっています。
次のforeachで順番に対応をとってリネームしていきます。
echo 'rename('の部分を有効にすることでどうリネームされているかを確認できます。
ありがとうございます。
コードまで詳しく書いていただき感謝します。
PHP5ではなかったので、scandir関数はありがたかったです。
参考にさせて頂きます。
ありがとうございます。
書いていただいたソースを参考にやってみたのですが、while(is_dir・・・の部分で
Fatal error: Maximum execution time of 30 seconds exceededと出てタイムアウト
してしまいます。
ちなみに頂きましたコードの前に該当ファイルは削除しています。
ヒントをいただけると助かります。