note 72644 modified in function.copy by danbrown
| From: | danbrown@php.net | Date: | Mon, 02 Mar 2009 17:54:09 +0000 |
| Subject: | note 72644 modified in function.copy by danbrown | ||
| References: | 1 | Groups: | php.notes |
| Request: | Send a blank email to php-notes+get-151356@lists.php.net to get a copy of this message | ||
This function creates a new filename to use for a copy of the given filename, its behaviour was
mostly sto^Wborrowed from how the OS X Finder (*1) does it.
Note it *doesn't* actually copy the file, it just returns the new name. I needed it to work
regardless of data source (filesystem, ftp, etc).
It also tries to match the current name as neatly as possible:
foo.txt -> foo copy.txt -> foo copy 1.txt -> foo copy 2.txt [etc]
foo.bar.baz.jpg -> foo.bar.baz copy.jpg
foobar -> foobar copy -> foobar copy 1 [etc]
".txt" -> .txt copy, and "txt." -> txt. copy
file.longextension -> file.longextension copy
It keeps trying until it finds a name that is not yet taken in $list, or until it looped 500 times
(change as needed).
If the renamed file becomes longer than max filename length, it starts chopping away at the end of
the part before where it adds " copy": reallylong...filename.txt -> reallylong...filena
copy.txt
<?php
// $orig = current name, of course
// $list = array of filenames in the target directory (if none given, it will still return a new
name)
// $max = max length of filename
function duplicate_name($orig, $list = array(), $max = 64) {
$ext = '';
$counter = 0;
$list = (array) $list;
$max = (int) $max;
$newname = $orig;
do {
$name = $newname; # name in, newname out
if (preg_match('/ copy$| copy \d+$/', $name, $matches)) {
// don't even check for extension, name ends with " copy[ digits]"
// preg hereunder matches anything with at least one period in the middle and an extension of
1-5 characters
}elseif (preg_match('/(.+)\.([^.]{1,5})$/', $name, $parts)) {
// split to name & extension
list($name, $ext) = array($parts[1], $parts[2]);
}
if (preg_match('/ copy (\d+)$/', $name, $digits)) {
$newname = substr($name, 0, - strlen($digits[1])) . ($digits[1] + 1);
# $cutlen is only used for the bit at the end where it checks on max filename length
$cutlen = 6 + strlen($digits[1]+1); // ' copy ' + digits
}elseif(preg_match('/ copy$/', $name, $digits)) {
$newname = $name . ' 1';
$cutlen = 7; // ' copy' + ' 1'
}else{
$newname = $name . ' copy';
$cutlen = 5; // ' copy'
}
if ($ext) {
$newname .= '.' . $ext;
$cutlen += strlen($ext) + 1;
}
if ($max > 0) {
if (strlen($newname) > $max) {
$newname = substr($newname, 0, max($max - $cutlen, 0)) . substr($newname, -$cutlen);
if (strlen($newname) > $max) {echo "duplicate_name() error: Can't keep the new
name under given max length.\n"; return false;}
}
}
if ($counter++ > 500) {echo "duplicate_name() error: Too many similarly named files or
infinite while loop.\n"; return false;}
} while (in_array($newname, $list));
return $newname;
}
?>
*1) The Finder seems to check the extension vs a list of known extensions, this function considers
it valid if it's 5 or fewer characters long.
ps. sorry for taking up so much space! :-)
--was--
This function creates a new filename to use for a copy of the given filename, its behaviour was
mostly sto^Wborrowed from how the OS X Finder (*1) does it.
Note it *doesn't* actually copy the file, it just returns the new name. I needed it to work
regardless of data source (filesystem, ftp, etc).
It also tries to match the current name as neatly as possible:
foo.txt -> foo copy.txt -> foo copy 1.txt -> foo copy 2.txt [etc]
foo.bar.baz.jpg -> foo.bar.baz copy.jpg
foobar -> foobar copy -> foobar copy 1 [etc]
".txt" -> .txt copy, and "txt." -> txt. copy
file.longextension -> file.longextension copy
It keeps trying until it finds a name that is not yet taken in $list, or until it looped 500 times
(change as needed).
If the renamed file becomes longer than max filename length, it starts chopping away at the end of
the part before where it adds " copy": reallylong...filename.txt -> reallylong...filena
copy.txt
<?php
// $orig = current name, of course
// $list = array of filenames in the target directory (if none given, it will still return a new
name)
// $max = max length of filename
function duplicate_name($orig, $list = array(), $max = 64) {
$ext = '';
$counter = 0;
$list = (array) $list;
$max = (int) $max;
$newname = $orig;
do {
$name = $newname; # name in, newname out
if (preg_match('/ copy$| copy \d+$/', $name, $matches)) {
// don't even check for extension, name ends with " copy[ digits]"
// preg hereunder matches anything with at least one period in the middle (that is,
// non-period characters on either side), and an extension of 1-5 characters
}elseif (preg_match('/(.+)\.([^.]{1,5})$/', $name, $parts)) {
// split to name & extension
list($name, $ext) = array($parts[1], $parts[2]);
}
if (preg_match('/ copy (\d+)$/', $name, $digits)) {
$newname = substr($name, 0, - strlen($digits[1])) . ($digits[1] + 1);
# $cutlen is only used for the bit at the end where it checks on max filename length
$cutlen = 6 + strlen($digits[1]+1); // ' copy ' + digits
}elseif(preg_match('/ copy$/', $name, $digits)) {
$newname = $name . ' 1';
$cutlen = 7; // ' copy' + ' 1'
}else{
$newname = $name . ' copy';
$cutlen = 5; // ' copy'
}
if ($ext) {
$newname .= '.' . $ext;
$cutlen += strlen($ext) + 1;
}
if ($max > 0) {
if (strlen($newname) > $max) {
$newname = substr($newname, 0, max($max - $cutlen, 0)) . substr($newname, -$cutlen);
if (strlen($newname) > $max) {echo "duplicate_name() error: Can't keep the new
name under given max length.\n"; return false;}
}
}
if ($counter++ > 500) {echo "duplicate_name() error: Too many similarly named files or
infinite while loop.\n"; return false;}
} while (in_array($newname, $list));
return $newname;
}
?>
*1) The Finder seems to check the extension vs a list of known extensions, this function considers
it valid if it's 5 or fewer characters long.
ps. sorry for taking up so much space! :-)
http://php.net/manual/en/function.copy.php