filesize() 返回位元組數,人類難以直觀閱讀。本文收集整理 8 種格式化風格(遞迴、迴圈、對數、查表、遞歸等),分析優缺點,並提供生產級封裝函數。
$unitIndex = floor(log($bytes, 1024)) → $value = $bytes / (1024 ** $unitIndex)。function formatSizeLog($bytes) {
if ($bytes == 0) return '0 Bytes';
$units = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
$i = floor(log($bytes, 1024));
return round($bytes / (1024 ** $i), 2) . ' ' . $units[$i];
}
function formatSizeLoop($bytes, $dec = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
$i = 0;
while ($bytes >= 1024 && $i < count($units) - 1) {
$bytes /= 1024;
$i++;
}
return round($bytes, $dec) . ' ' . $units[$i];
}
function formatSizeTable($bytes) {
static $thresholds = [
'GB' => 1073741824,
'MB' => 1048576,
'KB' => 1024,
'B' => 1,
];
foreach ($thresholds as $unit => $threshold) {
if ($bytes >= $threshold) {
return ($unit === 'B')
? $bytes . ' B'
: round($bytes / $threshold, 2) . ' ' . $unit;
}
}
}
function formatSizeRecursive($bytes, $unit = 'B') {
$units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
$idx = array_search($unit, $units);
if ($bytes < 1024 || $idx === count($units) - 1) {
return round($bytes, 2) . ' ' . $unit;
}
return formatSizeRecursive($bytes / 1024, $units[$idx + 1]);
}
function formatSizeIfElse($bytes) {
if ($bytes >= 1073741824) return round($bytes / 1073741824, 2) . ' GB';
if ($bytes >= 1048576) return round($bytes / 1048576, 2) . ' MB';
if ($bytes >= 1024) return round($bytes / 1024, 2) . ' KB';
return $bytes . ' B';
}
function formatSizeStruct($bytes, $dec = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
$i = 0;
while ($bytes >= 1024 && $i < count($units) - 1) {
$bytes /= 1024; $i++;
}
return ['size' => round($bytes, $dec), 'unit' => $units[$i]];
}
// 用法:[$size, $unit] = formatSizeStruct($bytes); echo "$size $unit";
function formatSizeSprintf($bytes) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$i = 0;
while ($bytes >= 1024 && $i < 4) { $bytes /= 1024; $i++; }
return sprintf('%.2f %s', $bytes, $units[$i]);
}
function formatSizePrecision($bytes) {
if ($bytes < 1024) return $bytes . ' B';
if ($bytes < 10240) return round($bytes / 1024, 2) . ' KB';
if ($bytes < 102400) return round($bytes / 1024, 1) . ' KB';
if ($bytes < 1048576) return round($bytes / 1024, 0) . ' KB';
if ($bytes < 10485760) return round($bytes / 1048576, 2) . ' MB';
if ($bytes < 104857600) return round($bytes / 1048576, 1) . ' MB';
if ($bytes < 1073741824) return round($bytes / 1048576, 0) . ' MB';
return round($bytes / 1073741824, 2) . ' GB';
}
1. 迴圈除法 : 0.18 秒 ← 推薦:均衡
2. 查表法 : 0.15 秒 ← 最快
3. 對數法 : 0.22 秒
4. 區間判斷 : 0.16 秒
5. 遞歸法 : 0.31 秒 ← 最慢(函數呼叫開銷)
6. 結構化陣列 : 0.19 秒
7. sprintf : 0.20 秒
8. 精細區間 : 0.17 秒
class FileSizeFormatter {
private const UNITS = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
private const BASE = 1024;
public static function format(int $bytes, int $decimals = 2, bool $iec = true): string {
if ($bytes === 0) return '0 ' . self::UNITS[0];
if ($bytes < 0) throw new InvalidArgumentException('Size cannot be negative');
$unitIndex = min((int)floor(log($bytes, self::BASE)), count(self::UNITS) - 1);
$value = $bytes / (self::BASE ** $unitIndex);
$unit = $iec ? self::UNITS[$unitIndex] : str_replace('i', '', self::UNITS[$unitIndex]);
return round($value, $decimals) . ' ' . $unit;
}
// 靜態快取 threshold 版本(極高頻調用)
public static function formatFast(int $bytes, int $decimals = 2): string {
static $thresholds = null;
if ($thresholds === null) {
$thresholds = [];
$val = 1;
foreach (self::UNITS as $unit) {
$thresholds[$unit] = $val;
$val *= self::BASE;
}
}
foreach (array_reverse($thresholds, true) as $unit => $threshold) {
if ($bytes >= $threshold) {
return round($bytes / $threshold, $decimals) . ' ' . $unit;
}
}
return $bytes . ' B';
}
}
// 使用
echo FileSizeFormatter::format(49957289167); // 46.53 GiB
echo FileSizeFormatter::formatFast(123456789); // 117.74 MiB
formatFast),避免重複 log/pow 運算。['size' => 46.53, 'unit' => 'GiB'],前端按 locale 格式化。