PHP - search and replace then sort and echo - php

I've managed (well, stackoverflow has shown me how) to search a directory on my server and echo and image. The trouble is the images in the folder are named by an IP camera yy_mm_dd_hh_mm where dd (and other date digits) have either one or two digits. I need them to have a preceeding zero so that I don't end up with, for example, an image taken at 9:50am being treated as a higher value than the photo taken more recently, at 10:05am. (I want it to treat it as 09_50 and 10_05 to fix the issue).
I've looked at search and replace but cannot get this to work within my current code:
function webcam_image () {
foreach (glob( "../camera/IPC_IPCamera*.jpg") as $f ) {
$list[] = $f;
}
sort($list);
echo array_pop($list);
}
example file = IPC_IPCamera_13_7_24_9_57_45.jpg
any help would be much appreciated!
Thanks
Ali

I would ignore the file name altogether and use a DirectoryIterator, fetching the files actual modified date. Something like this might work.
$files = array();
$iterator = new \DirectoryIterator('/path/to/dir');
foreach ($iterator as $file) {
if(! $file->isDot()) $files[$file->getMTime()] = $file->getFilename();
}
ksort($files);
print_r($files);
Take a look here for more information: http://php.net/manual/en/class.directoryiterator.php

If I have understood you correctly, you could handle this by preg_replaceing the files.
So you could loop through your files and do the following
$newFilename = preg_replace('/_([0-9])_/', '_0$1_', $oldFilename);
rename($oldFilename, $newFilename);

You can try something like:
function webcam_image () {
foreach (glob( "../camera/IPC_IPCamera*.jpg") as $f ) {
$digits = explode('_', substr(substr($f, 13), 0, -4));
foreach($digits as &$digit){
$digit = sprintf("%02d", $digit);
}
unset($digit);
$list[] = 'IPC_IPCamera_' . implode('_', $digits) . '.jpg';
}
sort($list);
echo array_pop($list);
}

You can use sprintf()
$list[] = $f;
with the following
$list[] = sprintf("%02s", $f);

Related

Omit certain results from a random array PHP

I'm trying to get 2 random results from an array of files in the "related" directory. I've managed to pull two results randomly from the directory but I need to avoid certain results depending on a variable relating to a specific file name.
My code so far is:
$foo = "bar.php";
function random_file($dir) {
$files = opendir($dir . '/*.php');
$rand_files = array_rand($files, 2);
return array(include $files[$rand_files[0]], include $files[$rand_files[1]]);
}
list($file_1,$file_2) = random_file("related");
I'm trying to pull two random results but avoid the file: bar.php. Does anyone know of a way to omit certain results from the array as I can't find anything online even close?
You can use glob function with a specific regex to only select names that are a match for you. This will limit your initial $files variable to results that do satisfy your condition and you can continue and do the random sampling without modifications.
// entries containing foo will not be included
function random_file($dir) {
$files = glob("^(?!bar.php)*");
$rand_files = array_rand($files, 2);
return array(include $files[$rand_files[0]], include $files[$rand_files[1]]);
}
list($file_1,$file_2) = random_file("related");
You also need to consider directories such as '.' and '..'. I think a switch statement and an unset() for those values in your overall array would work. Then once you have an array with files not including what you don't want. You can then pull two randoms and return that array.
This code may not be 100% perfect but should get you in the right direction.
function random_file($dir) {
$fileArray = array();
if (is_dir($dir)) {
if ($dh = opendir($dir . '/*.php')) {
while (($file = readdir($dh)) !== false) {
$fileArray = array_push($fileArray, $file)
}
for( $i = 0; $i < count($fileArray); $i++ ) {
switch($fileArray) {
case '.':
unset($array[$i]);
break;
case '..':
unset($array[$i]);
break;
case 'bar.php':
unset($array[$i]);
break;
}
}
}
closedir($dh);
}
$rand_files_keys = array_rand($fileArray, 2);
$rand_files = $fileArray[$rand_files_keys[0]];
$rand_files = $fileArray[$rand_files_keys[1]];
return $rand_files;
try this. It will keep randomizing your array until it selects two records excluding the bar.php
$rand_files = array_rand($files, 2);
while(in_array("bar.php",$rand_files))
{
$rand_files = array_rand($files, 2);
}

PHP - get single file based on user

I'm having trouble getting my code to work the way I want.
I'm using scandir to get all files from the directory. This gives me a list with pdf files linked to a product, but the problems comes with the posibllity of pdf files multiple languages. Like so:
1096_EN.pdf
867_PT.pdf
914_EN.pdf
914_NL.pdf
Before _ is ID and after language. And I want the user to only see one file per product.
my code looks likes this:
$files = scandir($dir);
foreach ($files as $file)
{
$exp_file = explode("_", $file);
// check file for given ID
if($exp_file[0] == $_GET['iD']){
// check file for userlanguage
if($exp_file[1] == $lang){
echo $file;
}
// check file in english
elseif($exp_file[1] == "EN"){
echo $file;
}
// return available file in other language
else{
echo $file;
}
}
}
In case of 914 and NL the code returns two files. In case of 914 and PT i only get 1 file, 914_EN.pdf and in case of 867 and NL there will be zero files.
What is the best way to filter my files and return the best matched file? I personally think the error is in the for loop, but I cant find a proper way out..
thanks
If you want to have just the single items, you should keep a backlog of which you have already processed, as the foreach loop will go from for example 914_EN.pdf to 914_NL.pdf, while the checks have already been completed for 914_EN.pdf, so when you get to 914_NL.pdf, it just reruns the checks and thinks it is okay.
if working with multiple same values, you can first cleanse the array to get what you wanted. You can take a look at this, if this what you want. Cheers!
$files = array("1096_EN.pdf", "867_PT.pdf", "914_EN.pdf", "914_NL.pdf");
$new_exp_file = array();
foreach ($files as $file) {
$exp_file = explode("_", $file);
$new_exp_file[] = $exp_file[0];
}
$new_exp_file_arr_ = array_values(array_unique($new_exp_file));
for($i = 0, $file_ctr = count($new_exp_file_arr_); $i < $file_ctr; $i++) {
if($new_exp_file_arr_[$i] == "914") {
echo $new_exp_file_arr_[$i] . "<br>";
echo "<ul>";
foreach ($files as $file) {
$exp_file = explode("_", $file);
if($new_exp_file_arr_[$i] == $exp_file[0]) {
echo "<li>" . $exp_file[1] . "</li>";
}
}
echo "</ul>";
}
}
this seems to work for me? Using a regex probably not as efficient as the above methods though.
$_GET['iD'] = 1096;
$ptn = "^((\d+)\_([a-zA-Z]+)\.([a-zA-Z]+))^";
$aFiles = array('1096_EN.pdf','867_PT.pdf','914_EN.pdf','914_NL.pdf');
$lang = "EN";
foreach ($aFiles as $sFileName)
{
preg_match($ptn, $sFileName, $aFileParts);
var_dump($aFileParts);
// check file for given ID
if($aFileParts[2] == $_GET['iD']){
// check file for userlanguage
if(strtolower($aFileParts[3]) == strtolower($lang)){
echo $sFileName;
break;
}
// return available file in other language
else{
echo $sFileName;
}
}
}
I've solved my problem by the following:
if(glob($_GET['iD']."_".$_GET['t']."*.pdf"))
{
$file = glob($_GET['iD']."_".$_GET['t']."*.pdf");
echo $file[0];
}
else
{
if(glob($_GET['iD']."_EN*.pdf"))
{
$file = glob($_GET['iD']."_EN*.pdf");
echo $file[0];
}
else
{
$file = glob($_GET['iD']."*.pdf");
echo $file[0];
}
}
No more looping, just checking for different files with wildcards. Works like a charm. I.m.o. much cleaner with larger lists of files..

foreach two arrays in php?

I have two arrays like this
$slike = Array('artist2-1.jpg', 'artist2-2.jpg', 'artist2-3.jpg', 'artist2-4.jpg');
$slikethumb = Array('artist2-1_thumb.jpg',
'artist2-2_thumb.jpg',
'artist2-3_thumb.jpg',
'artist2-4_thumb.jpg');
When I foreach two arrays I want to get this output on view
echo'<a href='.$slike.'><img src='.$slikethumb.'></a>';
I know how to foreach one array, but what I have to do to combine two arrays in one foreach?
If those arrays are sorted correctly and both have same number of elements:
foreach($slike as $i => $value){
echo'<a href='.$value.'><img src='.$slikethumb[$i].'></a>';
}
Edit:
Considering your related question, You might want to think about scanning your directoy using glob and a more specific pattern, something along the lines of:
$sLike = glob('{0,1,2,3,4,5,6,7,8,9}.jpg',GLOB_BRACE);
//selects all .jpg files with names ending with number.jpg, so no _thumb.jpg files
Then, with this in mind and in light of your comment. How to remove the file extensions:
$sLike = array();//the array from dir scan
$clean = array();
foreach($sLike as $file)
{
$clean[] = substr($file, 0, strpos($file, '.')*-1);//get file name
}
$format = '<img src="%1$s_thumb.jpg" />';//output format
foreach($clean as $like)
{
    printf($format, $like);
}
Or, with a more liberal pattern used for the glob call, and if you want to go with option 2 (the one with different extensions per file):
$sLike = array();//the array from dir scan
$clean = array();
foreach($sLike as $file)
{
$endOffset = strpos($file, '.')*-1;
$clean[] = array(
substr($file, 0, $endOffset),//get file name
substr($file,$endOffset)//get extension, includes the . char!
);
}
$format = '<img src="%1$s_thumb%2$s" />';//output format
foreach($clean as $like)
{
echo vsprintf($format, $like);
}
That's about it...
What I'd do is use but a single array. The base string is the same for both images:
$slike=array(
'artist2-1',
'artist2-2',
'artist2-3',
'artist2-4'
);//remove extensions
$format = '<img src="%1$s_thumb.jpg" />';//output format
foreach($slike as $like)
{
printf($format, $like);
}
That will give you what you want, without there ver being a need for 2 arrays, as you can see on this codepad.
Benefits of this approach:
only constructs 1 array, not 2
uses printf which translates internally to a fprintf(stdout, "<your format>", zval*$like); in C, which is fast.
No excess fat: there's very little repetition. You could even change the format to artist2-%s.jpg"><img src="artist2-%s_thumb.jpg" />, and only stores the numbers in the array, but that's taking it absurdly far.
Short, and concise code
Of course, there are caveats:
less readable & maintainable code
If ever you add 1 image with a different extension (png), you'll have to refactor...
The choice is yours. If different extensions is likely to happen, you could change the code above to something like:
$slike=array(
array('artist2-1','jpg'),
array('artist2-2','png'),
array('artist2-3','gif'),
array('artist2-4','jpeg'),
);//remove extensions
$format = '<img src="%1$s_thumb.%2$s" />';//output format
foreach($slike as $like)
{
echo vsprintf($format, $like);
}
As you can see on this codepad, that'll work like a charm, too
If same number of elements in both array & sorted then,
foreach($slikethumb as $key => $val) {
echo '<a href='.$slike[$key].'><img src='.$val.'></a>';
}
You can use for loop
for($i = 0; $i< count($slike); $i++)
{
$slike_val = $slike[$i];
$slikethumb_val = $slikethumb[$i];
echo'<a href='.$slike_val.'><img src='.$slikethumb_val.'></a>';
}
You will try
foreach($slike as $key=>$slike_val)
echo'<a href='.$slike_val.'><img src='.$slikethumb[$key].'></a>';
Try like this:
<?php
$slike = array('artist2-1.jpg', 'artist2-2.jpg', 'artist2-3.jpg', 'artist2-4.jpg');
$slikethumb = array('artist2-1_thumb.jpg', 'artist2-2_thumb.jpg', 'artist2-3_thumb.jpg', 'artist2-4_thumb.jpg');
foreach($slike as $key1 => $value1)
{
echo $value1."--------->".$slikethumb[$key1]."<br />";
}
?>
Thanks
foreach ($slike as $key=>$img) {
print '<a href='.$img.'><img src='.$slikethumb[$key].'></a>';
}
Or if you want one which is not based on the ordering....
foreach ($slike as $key=>$img) {
list($name, $ext)=explode('.', $img);
$thumb=$name . '_thumb.' . $ext;
if (in_array($thumb, $slikethumb)) {
print '<a href='.$img.'><img src='.$thumb.'></a>';
} else {
...?
}
}

Get latest file trailing number in php

I have something like this in my directory:
slider-1.jpg
slider-2.png
slider-4.gif
slider-8.png
slider-11.gif
Now is there a way to get the last trailing number of "slider" images ?
I need to get this so the next uploaded image should be named slider-12.xxx
I tried for each loop, but it obviously gets me slider-3 as a next name which is false in my case :)
ok, here is how I did it
foreach ($files as $filename) {
$filesarr[]= $filename;
$namearr = explode('.', $filename); //I had to grab this piece as well during the project, so this is main reason not usin Louis proposal.... i needed that array for something else...
$numbers = explode('-', $namearr[1]);
$compare[]=$numbers[2];
}
sort($compare, SORT_NUMERIC);
$assigned_number = end($compare) + 1;
You could try something like this:
$max = 0;
foreach($files AS $file) {
$max = max($max, filter_var($file, FILTER_SANITIZE_NUMBER_INT));
}
$max++;
or this:
natcasesort($files);
$max = filter_var(array_slice($files, -1), FILTER_SANITIZE_NUMBER_INT) + 1;

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.

Categories