【发布时间】:2011-07-13 05:58:12
【问题描述】:
我知道PHP 中有很多内置函数可用于获取文件大小,其中一些是:filesize、stat、ftell 等。
我的问题在于ftell,这很有趣,它会从文件中返回文件指针的整数值。
是否可以使用ftell 函数获取文件的大小?如果是,那告诉我怎么做?
场景:
- 系统(代码)以“a”模式打开现有文件以附加内容。
- 文件指针指向行尾。
- 系统将内容更新到文件中。
- 系统使用
ftell计算文件大小。
【问题讨论】:
我知道PHP 中有很多内置函数可用于获取文件大小,其中一些是:filesize、stat、ftell 等。
我的问题在于ftell,这很有趣,它会从文件中返回文件指针的整数值。
是否可以使用ftell 函数获取文件的大小?如果是,那告诉我怎么做?
场景:
ftell 计算文件大小。【问题讨论】:
fstat 确定文件大小没有任何杂技:
$f = fopen('file', 'r+');
$stat = fstat($f);
$size = $stat['size'];
ftell 不能在使用 append("a") 标志打开文件时使用。此外,您必须先使用fseek($f, 0, SEEK_END) 查找文件末尾。
【讨论】:
fseek 上的信息成功了。我将fseek 与ftell 结合使用,这是最快的。
ftell() 可以告诉您文件中应该有多少字节,但不能告诉您实际有多少。 Sparse files 占用的磁盘空间比寻找到最后并告诉将返回的值更少。
【讨论】:
我写了一个基准来改进这个话题,为了避免人们争论存在某种 php/cache,我在另一个进程中创建了独特的文件。
这是一个我毫无疑问的新基准。
测试忽略 fopen 和 close 时间,因为用户要求最快的方法来计算已打开文件的大小。 每个测试运行 200 个文件。
在单独进程中创建文件的代码是本文的第一条评论。
<?php
class timeIt
{
static private $times = [];
static function new()
{
self::$times[] = hrtime(true);
}
static function stop()
{
self::$times[] = -1;
}
static function dif()
{
$dif = 0;
$sum = 0;
$i = count(self::$times) - 1;
if (self::$times[$i] === -1)
unset(self::$times[$i--]);
for ($i = count(self::$times) - 1; $i > 0; --$i) {
if (self::$times[$i - 1] === -1) {
$sum += $dif;
$dif = 0;
--$i;
continue;
}
$dif += self::$times[$i] - self::$times[$i - 1];
}
return $sum + $dif;
}
static function printNReset()
{
echo "diffTime:" . self::dif() . "\n\n";
self::reset();
}
static function reset()
{
self::$times = [];
}
}
function fseek_size_from_current($handle)
{
$current = ftell($handle);
fseek($handle, 0, SEEK_END);
$size = ftell($handle);
fseek($handle, $current);
return $size;
}
function fseek_size_from_start($handle)
{
fseek($handle, 0, SEEK_END);
$size = ftell($handle);
fseek($handle, 0);
return $size;
}
function uniqueProcessId()
{
return (string) hrtime(true);
}
function getUniqueForeignProcessFiles($quantity, $size)
{
$returnedFilenames = $filenames = [];
while ($quantity--){
$filename = uniqueProcessId();
$filenames[$filename] = $size;
$returnedFilenames[] = __DIR__ . DIRECTORY_SEPARATOR . $filename;
}
$data = base64_encode(json_encode($filenames));
$foreignCgi = __DIR__ . DIRECTORY_SEPARATOR . "createFileByNames.php";
$command = "php $foreignCgi $data";
if (shell_exec($command) !== 'ok')
die("An error ocurred");
return $returnedFilenames;
}
const FILESIZE = 20 * 1024 * 1024;
foreach(getUniqueForeignProcessFiles(200, FILESIZE) as $filename){
$handle = fopen($filename, 'r');
timeIt::new();
$size = fstat($handle)['size'];
timeIt::new();
timeIt::stop();
fclose($handle);
unlink($filename);
}
echo "**fstat**\n";
timeIt::printNReset();
foreach(getUniqueForeignProcessFiles(200, FILESIZE) as $filename){
$handle = fopen($filename, 'r');
timeIt::new();
$size = fseek_size_from_start($handle);
timeIt::new();
timeIt::stop();
fclose($handle);
unlink($filename);
}
echo "**fseek with static/defined**\n";
timeIt::printNReset();
foreach(getUniqueForeignProcessFiles(200, FILESIZE) as $filename){
$handle = fopen($filename, 'r');
timeIt::new();
$size = fseek_size_from_current($handle);
timeIt::new();
timeIt::stop();
fclose($handle);
unlink($filename);
}
echo "**fseek with current offset**\n";
timeIt::printNReset();
foreach(getUniqueForeignProcessFiles(200, FILESIZE) as $filename){
$handle = fopen($filename, 'r');
timeIt::new();
$size = filesize($filename);
timeIt::new();
timeIt::stop();
fclose($handle);
unlink($filename);
}
echo "**filesize after fopen**\n";
timeIt::printNReset();
foreach(getUniqueForeignProcessFiles(200, FILESIZE) as $filename){
timeIt::new();
$size = filesize($filename);
timeIt::new();
timeIt::stop();
unlink($filename);
}
echo "**filesize no fopen**\n";
timeIt::printNReset();
20MB 文件的结果,时间以纳秒为单位
fstat diffTime:2745700
使用静态/定义的 fseek diffTime:1267400
fseek 与当前偏移量 diffTime:983500
fopen 后的文件大小 diffTime:283052500
filesize no fopen diffTime:4259203800
1MB 文件的结果,以纳秒为单位:
fstat diffTime:1490400
使用静态/定义的 fseek diffTime:706800
fseek 与当前偏移量 diffTime:837900
fopen 后的文件大小 diffTime:22763300
filesize no fopen diffTime:216512800
以前这个答案有另一个基准,我删除了算法以让这个答案更清晰。 该算法使用由自己的进程创建的文件,假设是:
ftell + fseek 是 fstat['size'] 的 一半 时间,即使在另一个函数内部并且调用这两个函数两次。 fstat 是 速度较慢,因为它包含的信息不仅仅是文件大小, 因此,如果您需要代码旁边的其他信息,请检查 更改,只需坚持 fstat。
当前的基准测试表明该假设是有效的,即: **对于 1-20MB 的文件,fseek + ftell++ 比 fstat 快 2-2.8 倍。
随意运行您的基准测试并分享您的结果。
【讨论】:
感谢@Phihag,您在fseek 和ftell 上提供的信息让我能够以更好的方式计算尺寸。在此处查看代码:http://pastebin.com/7XCqu0WR
<?php
$fp = fopen("/tmp/temp.rock", "a+");
fwrite($fp, "This is the contents");
echo "Time taken to calculate the size by filesize function: ";
$t = microtime(true);
$ts1 = filesize("/tmp/temp.rock") . "\n";
echo microtime(true) - $t . "\n";
echo "Time taken to calculate the size by fstat function:";
$t = microtime(true);
$ts1 = fstat($fp) . "\n";
$size = $ts1["size"];
echo microtime(true) - $t . "\n";
echo "Time taken to calculate the size by fseek and ftell function: ";
$t = microtime(true);
fseek($fp, 0, SEEK_END);
$ts2 = ftell($fp) . "\n";
echo microtime(true) - $t . "\n";
fclose($fp);
/**
OUTPUT:
Time taken to calculate the size by filesize function:2.4080276489258E-5
Time taken to calculate the size by fstat function:2.9802322387695E-5
Time taken to calculate the size by fseek and ftell function:1.2874603271484E-5
*/
?>
【讨论】:
fopen 花费的时间。 (顺便说一句,我的建议是不是 filesize,而是fstat,它也在句柄上运行),所以你将苹果与橙子进行比较(访问句柄与路径文件系统)无论如何。此外,您没有考虑缓存。自然,当您调用fseek 时,文件大小已经确定并缓存了。
ftell是最好的。
filesize 到fstat/ftell 的形式比较苹果和橙子。另请注意,结果取决于一般计算机和缓存设置。例如,如果您的 CPU 缓存已经预热并包含 inode 结构,filesize(它必须首先找到 inode)会慢很多。另一方面,如果您处于必须从 HDD 读取文件的 inode(可能包含目录)的设置中,...
filesize 将比fopen+fseek+ftell 快。总而言之,既然您无论如何都在编写 php,我建议您只在重要的情况下确保代码的清晰性和性能。