PHP batch file renaming - php

I have a bunch of files named like this...
full-file(1).jpg
full-file(10).jpg
full-file(11).jpg
full-file(12).jpg
full-file(2).jpg
etc...
I'm trying to figure out the most efficient way to rename all these files using PHP so that they get renamed like this...
full-file0001.jpg
full-file0010.jpg
full-file0011.jpg
full-file0012.jpg
full-file0002.jpg
I've got as far as reading all the files from a folder and looping through them but not sure about the best way to remove the brackets and make the number 4 digits with leading 0.
$image_files = get_files($thumbpath);
foreach($image_files as $index=>$file) {
echo $file;
}

Use a regular expression to get the digit, and then zero-pad it using sprintf():
$image_files = get_files($thumbpath);
foreach($image_files as $index=>$file) {
// Capture \d+ into $matches[1]
preg_match('/\((\d+)\)/', $file, $matches);
// Pad it with %04d in sprintf()
$newfile = sprintf("full-file%04d.jpg", $matches[1]);
}
Example:
php > $file = 'full-file(12).jpg';
php > preg_match('/\((\d+)\)/', $file, $matches);
php > $newfile = sprintf("full-file%04d.jpg", $matches[1]);
php > echo $newfile;
// full-file0012.jpg
Update (for more flexible filenames):
To please the downvoter I can only assume wanted more flexible filenames, expand the regular expression:
$image_files = get_files($thumbpath);
foreach($image_files as $index=>$file) {
preg_match('/([^(]+)\((\d+)\)(.+)/', $file, $matches);
$newfile = sprintf("%s%04d%s", $matches[1], $matches[2], $matches[3]);
// And rename the file
if (!rename($file, $newfile)) {
echo "Could not rename $file.\n";
}
else echo "Successfully renamed $file to $newfile\n";
}
The pattern matches first, everything up to the the first ( with ([^(]+), followed by the number via (\d+), and everything remaining via (.*).

You can use a mixture of REGEXP (remove brackets) and string padding (to force four digits).
Note I use a replacement callback to do both operations in one place.
$files = array(
'full-file(1).jpg',
'full-file(10).jpg',
'full-file(11).jpg',
'full-file(12).jpg',
'full-file(2).jpg'
);
function pr_callback($match) {
return str_pad($match[1], 4, '0', STR_PAD_LEFT);
}
foreach($files as $file)
echo preg_replace_callback('/\((\d+)\)/', pr_callback, $file).'<br />';
Outputs:
full-file0001.jpg
full-file0010.jpg
full-file0011.jpg
full-file0012.jpg
full-file0002.jpg

I haven't seen anyone recommend sscanf() yet.
<?php
$files = array(
"full-file(1).jpg",
"full-file(10).jpg",
"full-file(11).jpg",
"full-file(12).jpg",
"full-file(2).jpg",
);
foreach ($files as $file) {
$n = sscanf($file, "full-file(%d).jpg");
printf("full-file%04d.jpg\n", $n[0]);
}
returns:
full-file0001.jpg
full-file0010.jpg
full-file0011.jpg
full-file0012.jpg
full-file0002.jpg
This only works if "full-file" is the actual name of your file, of course. sscanf() is not a regex parser, it merely extracts data using printf()-style format strings ... though it does do some more advanced format recognition than is documented at http://php.net/sscanf . If you need to handle other filenames, you can extend the format string:
<?php
$files = array(
"this-file(1).jpg",
"full-file(10).jpg",
"foo(11).jpg",
"blarg(12).jpg",
"full-file(2).jpg",
);
foreach ($files as $file) {
$n = sscanf($file, "%[a-z-](%d).jpg");
printf("%s%04d.jpg\n", $n[0], $n[1]);
}
returns:
this-file0001.jpg
full-file0010.jpg
foo0011.jpg
blarg0012.jpg
full-file0002.jpg

Assuming your files aren't actually called full-file(0000).jpg:
<?php
$arr = array('full-file(1).jpg',
'full-file(10).jpg',
'full-file(11).png',
'full-file(12).jpg',
'full-file(2).gif',
'abc(12345).jpg',
'def(99).jpg',
'xzy-file(100).jpg');
function format_filename($matches){
return $matches[1].sprintf("%04d",$matches[3]).'.'.$matches[5];
}
function process_array(&$value){
$value = preg_replace_callback('/^(.*?)(\()(\d+)(\)).(jpg|png|gif)/','format_filename',$value);
}
array_walk($arr,'process_array');
print_r($arr);
/*
Array
(
[0] => full-file0001.jpg
[1] => full-file0010.jpg
[2] => full-file0011.png
[3] => full-file0012.jpg
[4] => full-file0002.gif
[5] => abc12345.jpg
[6] => def0099.jpg
[7] => xzy-file0100.jpg
)
*/
?>

Use code:
preg_match('/^(.*?)\((\d+)\)(.*)$/', $name, $m);
$name = sprintf("%s%04d%s", $m[1], $m[2], $m[3]);
See and test it here.

You will need str-pad(). A sample soon...
EDIT 1: solution using str_pad and preg_replace_callback.
OBS: Anonymous functions only in php5.3+.
foreach ($image_files as $file)
{
$o = preg_replace_callback(
"|\((\d+)\)|", function($matches)
{
$r = str_pad($matches[1], 4, '0', STR_PAD_LEFT);
return $r;
}
, $file);
echo $o . "\n";
}

Related

remove the extension from the key

How to remove the extension from the array key, ie .md.
It should look like this key: [about] => pages/about.md
Array:
Array:
(
[_desktop.md] => pages/_desktop.md
[about.md] => pages/about.md
[contact.md] => pages/contact.md
[errorpages] => Array
(
[403.md] => pages/errorpages/403.md
[404.md] => pages/errorpages/404.md
[500.md] => pages/errorpages/500.md
[503.md] => pages/errorpages/503.md
)
[home.md] => pages/home.md
[indexpage.md] => pages/indexpage.md
)
Code:
function generatePathTree($dir) {
$pathstack = array($dir);
$contentsroot = array();
$contents = &$contentsroot;
while ($path = array_pop($pathstack)) {
$contents[basename($path)] = array();
$contents = &$contents[basename($path)];
foreach (scandir($path) as $filename) {
if ('.' != substr($filename, 0, 1)) {
$newPath = $path.'/'.$filename;
if (is_dir($newPath)) {
array_push($pathstack, $newPath);
$contents[basename($newPath)] = array();
} else {
$contents[basename($filename)] = $newPath;
}
}
$contentsroot = preg_replace("/\\.[^.]*$/", "", basename($filename));
}
}
return $contentsroot[basename($dir)];
}
I tried so:
$contentsroot = preg_replace("/\\.[^.]*$/", "", basename($filename));
But alas.
How to do?
In this code block (This is where filename gets assigned instead of dir):
else {
$fileExtRemoved = preg_replace("/[\.](.*)/", "", $filename);
$contents[basename($fileExtRemoved)] = $newPath;
}
Your regexp is a little bit off, this is the correct one:
https://3v4l.org/mMPrt
You can assigne the old value to new key and then unset the old key
$arr['about'] = $arr['about.md'];
unset($arr['about.md']);
It's three characters on all of them?
So why not just use sub_str($key , 0, -3);?
If it's always three chars it should work, and as I see it, it seems to be three.
So maybe:
$contents[basename(substr($filename,0,-3))];
Requirement: remove any part of the array key string after the first . if it exists in the string.
Solution:
list($key,) = explode('.', $old_key);
$arr[$key] = $arr[$old_key];
unset($arr[$old_key]);
A side note: there must be a better way to do the recursive loop. But a quick look suggests that array_walk_recursive only operates on the leaf nodes; array_map and array_reduce don't have recursive variants and they only take values anyway. Since you didn't explicitly ask for this, I'll stop there.

How can I remove the file extension from each string in a PHP array?

I have an array of strings, each string containing the name of an image file:
$slike=(1.jpg,253455.jpg,32.jpg,477.jpg);
I want new array, to look like this:
$slike=(1,253455,32,477);
How can I remove the file extension from each string in this array?
If you're working with filenames, use PHP's built in pathinfo() function. there's no need to use regex for this.
<?php
# your array
$slike = array('1.jpg','253455.jpg','32.jpg','477.jpg');
# if you have PHP >= 5.3, I'd use this
$slike = array_map(function($e){
return pathinfo($e, PATHINFO_FILENAME);
}, $slike);
# if you have PHP <= 5.2, use this
$slike = array_map('pathinfo', $slike, array_fill(
0, count($slike), PATHINFO_FILENAME
));
# dump
print_r($slike);
Output
Array
(
[0] => 1
[1] => 253455
[2] => 32
[3] => 477
)
Using preg_split in foreach
foreach ($slike as &$value) {
$split = preg_split('/\./', $value);
$value = $split[0]
}
Change the regex to /[^.]+/ to include a.b.jpg as well.
Regular expressions are your friend on this one. I'm assuming that by brackers, you mean an array.
$k = count($slike);
for ($i = 0; $i < $k; $i++) {
$extpos = strrpos($slike[$i],".");
$slike[$i] = substr($slike[$i],0,$extpos);
}
This is no longer regex-dependent, and benchmarks faster than pathinfo().
Try this one:
$slike = array('1.jpg','253455.jpg','32.jpg','477.jpg');
$slike = array_map(function($e){
$e = explode('.', $e);
return $e[0];
}, $slike);
echo "<pre>";
print_r($slike);
echo "</pre>";

list all defined constants from a file in php

I have some php files that includes some language constants
define("_SEARCH","Search");
define("_LOGIN","Login");
define("_WRITES","writes");
define("_POSTEDON","Posted on");
define("_NICKNAME","Nickname");
now I need to read each file and list all constants and their values
and to return an output like this :
constant name :
value is :
so I think there should be function to list all defined constants of a given php file.
I'm aware of functions like token_get_all or get_defined_constants but i wasn't able to do it.
If the files do contain nothing but define statements, you can use get_defined_constants:
function getUserDefinedConstants() {
$constants = get_defined_constants(true);
return (isset($constants['user']) ? $constants['user'] : array());
}
$constantsBeforeInclude = getUserDefinedConstants();
include('file.php');
$constantsAfterInclude = getUserDefinedConstants();
$newConstants = array_diff_assoc($constantsAfterInclude, $constantsBeforeInclude);
What it does is basically: get_defined_constants(true) gives us an array of arrays with all available constants, sorted by sections (core, user, ..) - the array under the key 'user' gives us all user-defined constants that we defined in our php code using define, up to that point. array_diff_assoc gives us the difference between this array before and after the file got included.. and that is exactly a list of all constants that got defined in that specific file (as long as there is none of the declarations a duplicate, meaning a constant with that exact name has been defined before - but this would cause an error anyway).
this is the php script you need:
<?php
//remove comments
$Text = php_strip_whitespace("your_constants_file.php");
$Text = str_replace("<?php","",$Text);
$Text = str_replace("<?","",$Text);
$Text = str_replace("?>","",$Text);
$Lines = explode(";",$Text);
$Constants = array();
//extract constants from php code
foreach ($Lines as $Line) {
//skip blank lines
if (strlen(trim($Line))==0) continue;
$Line = trim($Line);
//skip non-definition lines
if (strpos($Line,"define(")!==0) continue;
$Line = str_replace("define(\"","",$Line);
//get definition name & value
$Pos = strpos($Line,"\",\"");
$Left = substr($Line,0,$Pos);
$Right = substr($Line,$Pos+3);
$Right = str_replace("\")","",$Right);
$Constants[$Left] = $Right;
}
echo "<pre>";
var_dump($Constants);
echo "</pre>";
?>
The result will be something similar to this:
array(5) {
["_SEARCH"]=>
string(6) "Search"
["_LOGIN"]=>
string(5) "Login"
["_WRITES"]=>
string(6) "writes"
["_POSTEDON"]=>
string(9) "Posted on"
["_NICKNAME"]=>
string(8) "Nickname"
}
Late to the game here but I had a similar issue. You could use an include() substitute/wrapper function that logs constants in an accessible global array.
<?php
function include_build_page_constants($file) {
global $page_constants ;
$before = get_defined_constants(true);
include_once($file);
$after = get_defined_constants(true);
if ( isset($after['user']) ) {
if ( isset($before['user']) ) {
$current = array_diff_assoc($after['user'],$before['user']);
}else{
$current = $after['user'];
}
$page_constants[basename($file)]=$current;
}
}
include_and_build_page_constants('page1.php');
include_and_build_page_constants('page2.php');
// test the array
echo '<pre>'.print_r($page_constants,true).'</pre>';
?>
This will result in something like:
Array
(
[page1.php] => Array
(
[_SEARCH] => Search
[_LOGIN] => Login
[_WRITES] => writes
[_POSTEDON] => Posted on
[_NICKNAME] => Nickname
)
[page2.php] => Array
(
[_THIS] => Foo
[_THAT] => Bar
)
)
Assuming that you want to do this on runtime, you should take a look at PHP Reflection, specifically at the ReflectionClass::getConstants() which lets you do exactly what you seem to want.
I too had the same problem. I went from jondinham's suggestion, but I prefer to use regex, as it is a bit easier to control and flexible. Here's my version of the solution:
$text = php_strip_whitespace($fileWithConstants);
$text = str_replace(array('<?php', '<?', '?>'), '', $text);
$lines = explode(";", $text);
$constants = array();
//extract constants from php code
foreach ($lines as $line) {
//skip blank lines
if (strlen(trim($line)) == 0)
continue;
preg_match('/^define\((\'.*\'|".*"),( )?(.*)\)$/', trim($line), $matches, PREG_OFFSET_CAPTURE);
if ($matches) {
$constantName = substr($matches[1][0], 1, strlen($matches[1][0]) - 2);
$constantValue = $matches[3][0];
$constants[$constantName] = $constantValue;
}
}
print_r($constants);

PHP how to get all files(only html files) in all subdirectories and index each html page

For a homework assignment, I have to get all the .htm and .html files in the current and all sub directories, and I have to index them by counting all the words that appear in the files individually.
Here is how I would count the file once I find an html file in a directory:
$file = '.html';
$index = indexer($file);
echo '<pre>'.print_r($index,true).'</pre>';
function indexer($file) {
$index = array();
$find = array('/\r/','/\n/','/\t/','!',',','.','"',';', ':');
$replace = array(' ',' ',' ',' ',' ',' ',' ',' ',' ');
$string = file_get_contents($file);
$string = strip_tags($string);
$string = strtolower($string);
$string = str_replace($find, $replace, $string);
$string = trim($string);
$string = explode(' ', $string);
natcasesort($string);
$i = 0;
foreach($string as $word) {
$word = trim($word);
$ignore = preg_match('/[^a-zA-Z]/', $word);
if($ignore == 1) {
$word = '';
}
if( (!empty($word)) && ($word != '') ) {
if(!isset($index[$i]['word'])) {
$index[$i]['word'] = $word;
$index[$i]['count'] = 1;
} elseif( $index[$i]['word'] == $word ) {
$index[$i]['count'] += 1;
} else {
$i++;
$index[$i]['word'] = $word;
$index[$i]['count'] = 1;
}
}
}
unset($work);
return($index);
}
I just need to figure out first how to find all the htm or html files in the directories and then start using the above code on each htm/html file. Any help will be appreciated, thanks!
Well, because this is a homework assignment, I won't give you the code. But I can point you in the right direction. Usually for this type of thing, people with use a recursive function. Where a function calls itself.
This function should do the following:
Count all the lines of all the htm, and html files in the current directory.
Add these numbers up, and then add them to a global variable outside the function (just use global, you could return the number of lines each call, and add them up, but that is a pain in the butt)
call this function again for every folder in the current directory (just loop through them)
once you are back at the very start, reset the global variable, and return its value
The RecursiveDirectoryIterator is the best class in PHP to do this. It's flexible and fast.
Other alternative methods (not recursive) are described in "Directory to array with PHP". In my answer to that question, I timed the different methods given by other answers, but all of the solutions in PHP code are slower than using the PHP's SPL classes.
Here's an alternative using RecursiveIteratorIterator, RecursiveDirectoryIterator and pathinfo().
<?php
$dir = '/';
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir), RecursiveIteratorIterator::CHILD_FIRST);
foreach ( $iterator as $path )
if ( $path->isFile() && preg_match('/^html?$/i', pathinfo($path->getFilename(), PATHINFO_EXTENSION)) )
echo $path->getPathname() . PHP_EOL;
If you need to get the current working directory, you can use getcwd() (i.e. $dir = getcwd();).
To get the length of the content, you can do a few things. You could retrieve the contents of the file using file_get_contents and use strlen to calculate the length or str_word_count to count the words. Another option could be to use $path->getSize().
If you use an array to store the names and the sizes, you can then use a custom function and uasort to sort the array by sizes.
A more complete example:
<?php
function sort_by_size($a, $b)
{
if ( $a['size'] == $b['size'] )
return 0;
return ( $a['size'] < $b['size'] ? -1 : 1 );
}
$dir = '/';
$files = array();
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir), RecursiveIteratorIterator::CHILD_FIRST);
foreach ( $iterator as $path )
if ( $path->isFile() && preg_match('/^html?$/i', pathinfo($path->getFilename(), PATHINFO_EXTENSION)) )
$files[] = array(
'name' => $path->getPathname(),
'size' => $path->getSize()
);
uasort($files, sort_by_size);
The $files array can then be looped through using a foreach loop. It will contain both the pathname and the size.
Try Using glob function.
$files = glob('*.htm*');
foreach($files as $file) {
//code here
}
Edited:
function readDir($path) {
$files = glob($path . '*.*');
foreach ($files as $file) {
if (is_dir($file)) {
$html_files = array_merge((array) readDir($file . '/'), (array) $html_files);
}
if (in_array(strtolower(end(explode('.', $file))), array('html', 'htm'))) {
$html_files[] = $file;
}
}
return $html_files;
}
Just edited the answer, Try this. (Note: I haven't Not tested the code on any site.)
Thanks
Do you have any restrictions on the functions/classes you can use? If not, then check out RecursiveDirectoryIterator it will let you go through dirs recursively iterating over all the items in the directory. You could then match the extension on each item and if it matches basically do your counting.
An alternative approach to this would be to use glob while iterating over the directories which allows you to do a *.html search like you would use with with the *nix utility find.
As far as counting you might want to take look at str_word_count.

Finding tags in query string with regular expression

I have to set some routing rules in my php application, and they should be in the form
/%var/something/else/%another_var
In other words i beed a regex that returns me every URI piece marked by the % character, String marked by % represent var names so they can be almost every string.
another example:
from /%lang/module/controller/action/%var_1
i want the regex to extract lang and var_1
i tried something like
/.*%(.*)[\/$]/
but it doesn't work.....
Seeing as it's routing rules, and you may need all the pieces at some point, you could also split the string the classical way:
$path_exploded = explode("/", $path);
foreach ($path_exploded as $fragment) if ($fragment[0] == "%")
echo "Found $fragment";
$str='/%var/something/else/%another_var';
$s = explode("/",$str);
$whatiwant = preg_grep("/^%/",$s);
print_r($whatiwant);
I don’t see the need to slow down your script with a regex … trim() and explode() do everything you need:
function extract_url_vars($url)
{
if ( FALSE === strpos($url, '%') )
{
return $url;
}
$found = array();
$parts = explode('/%', trim($url, '/') );
foreach ( $parts as $part )
{
$tmp = explode('/', $part);
$found[] = ltrim( array_shift($tmp), '%');
}
return $found;
}
// Test
print_r( extract_url_vars('/%lang/module/controller/action/%var_1') );
// Result:
Array
(
[0] => lang
[1] => var_1
)
You can use:
$str = '/%lang/module/controller/action/%var_1';
if(preg_match('#/%(.*?)/[^%]*%(.*?)$#',$str,$matches)) {
echo "$matches[1] $matches[2]\n"; // prints lang var_1
}

Categories