首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 獲取人類可讀檔案大小:兩種優雅實現與效能對比

     編輯:PHP 相關知識

將位元組數轉為「KB、MB、GB」等人類可讀格式是常見需求。本文提供兩種風格迥異但效果相同的實現:對數數學法 (一行式) 與 迭代除法 (直觀易讀),並分析效能與邊界情況。

方案一:對數數學法 (極簡一行)

<?php
/**
 * 位元組轉人類可讀大小 (對數法)
 * @param int|float $bytes 位元組數
 * @param int $decimals 小數位數
 * @return string
 */
function humanFilesizeLog(float $bytes, int $decimals = 2): string {
    if ($bytes <= 0) return '0 B';
    $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    $exp = (int)floor(log($bytes, 1024));
    $exp = min($exp, count($units) - 1); // 防止超出陣列
    return round($bytes / (1024 ** $exp), $decimals) . ' ' . $units[$exp];
}

// 測試
echo humanFilesizeLog(0);              // 0 B
echo humanFilesizeLog(500);            // 500 B
echo humanFilesizeLog(1024);           // 1 KB
echo humanFilesizeLog(1536);           // 1.5 KB
echo humanFilesizeLog(1024**2);        // 1 MB
echo humanFilesizeLog(1024**3 * 1.5);  // 1.5 GB
echo humanFilesizeLog(1024**5);        // 1 PB
?>

核心邏輯

  • log($bytes, 1024) 計算以 1024 為底的對數,直接得到單位指數 (0=B, 1=KB, 2=MB...)。
  • floor() 取整,min() 限制最大指數防止陣列越界。
  • 1024 ** $exp (PHP 5.6+) 計算除數,等同 pow(1024, $exp)。

方案二:迭代除法 (直觀、好維護)

<?php
/**
 * 位元組轉人類可讀大小 (迭代法)
 * @param int|float $bytes
 * @param int $decimals
 * @return string
 */
function humanFilesizeIter(float $bytes, int $decimals = 2): string {
    $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    $i = 0;
    while ($bytes >= 1024 && $i < count($units) - 1) {
        $bytes /= 1024;
        $i++;
    }
    return round($bytes, $decimals) . ' ' . $units[$i];
}

優點

  • 邏輯直觀:不斷除以 1024 直到小於 1024 或單位用完。
  • 無浮點數學運算 (log, pow),極大數值下數值穩定性略好。
  • 易於擴展:如加入 KiB, MiB (IEC 標準 1024) vs KB, MB (SI 標準 1000) 切換邏輯。

效能對比 (PHP 8.2 基準測試)

<?php
$testValues = [0, 500, 1024, 1024**2, 1024**3, 1024**4, 1024**5, 1024**6];
$iterations = 100000;

$start = hrtime(true);
foreach ($testValues as $v) for ($i=0;$i<$iterations;$i++) humanFilesizeLog($v);
echo "Log  法: " . (hrtime(true)-$start)/1e9 . "s\n";

$start = hrtime(true);
foreach ($testValues as $v) for ($i=0;$i<$iterations;$i++) humanFilesizeIter($v);
echo "Iter 法: " . (hrtime(true)-$start)/1e9 . "s\n";
?>

典型輸出:

Log  法: 0.18s
Iter 法: 0.12s

結論:迭代法略快 (避免 log/pow 函數調用開銷),但差異微乎其微 (納秒級)。**選擇原則:團隊偏好數學優雅用 Log 法,偏好直觀維護用 Iter 法。**

進階:支援 SI (1000) 與 IEC (1024) 雙標準

<?php
function humanFilesize(float $bytes, int $decimals = 2, bool $iec = true): string {
    $base = $iec ? 1024 : 1000;
    $units = $iec
        ? ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
        : ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

    if ($bytes < $base) return $bytes . ' ' . $units[0];
    $exp = min((int)floor(log($bytes, $base)), count($units) - 1);
    return round($bytes / ($base ** $exp), $decimals) . ' ' . $units[$exp];
}

echo humanFilesize(1000);      // 1000 B (IEC) / 1 KB (SI)
echo humanFilesize(1024);      // 1 KiB (IEC) / 1.02 KB (SI)
echo humanFilesize(1000**3);   // 953.67 MiB (IEC) / 1 GB (SI)
?>

邊界情況處理清單

  • 0 或負數:直接返回 0 B (或拋出異常視業務而定)。
  • 超大數值 (超過 YB/YiB):min() 限制指數,返回最大單位 (如 12345 YB)。
  • 浮點精度:round() 處理小數,避免 1.999999 KB 顯示問題。
  • 輸入類型:函數簽名用 float 兼容 int,大檔案 (>2GB) 於 32 位系統需用 float 或 string 傳入。

實際專案整合建議

  1. 放入共用工具類 App\Utils\FileHelper::humanSize()。
  2. 配置 decimals 為常數 (如 FILE_SIZE_DECIMALS = 2) 統一風格。
  3. 若專案已用 Laravel,直接用 Str::formatFileSize() (內建 IEC 標準)。
  4. Symfony 可用 NumberFormatter::format() 配合 NumberFormatter::FILE_SIZE。

無論選哪種實現,核心都是「以 1024 (或 1000) 為底換算單位」。理解兩種算法本質,能讓你在 Code Review 時自信判斷代碼品質,也能根據團隊風格靈活選擇。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved